Back to skills
SkillHub ClubShip Full StackFull Stack

gpui

Build desktop UIs with GPUI and gpui-component. Use when working on node-gui or any GPUI-based desktop application.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
3
Hot score
80
Updated
March 20, 2026
Overall rating
C0.8
Composite score
0.8
Best-practice grade
B75.9

Install command

npx @skill-hub/cli install legacy3-wowlab-gpui

Repository

legacy3/wowlab

Skill path: .claude/skills/gpui

Build desktop UIs with GPUI and gpui-component. Use when working on node-gui or any GPUI-based desktop application.

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: legacy3.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install gpui into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/legacy3/wowlab before adding gpui to shared team environments
  • Use gpui for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: gpui
description: Build desktop UIs with GPUI and gpui-component. Use when working on node-gui or any GPUI-based desktop application.
---

# GPUI & gpui-component Reference

This document covers the essential patterns and APIs for building desktop UIs with:

- **gpui** from [zed-industries/zed](https://github.com/zed-industries/zed)
- **gpui-component** from [longbridge/gpui-component](https://github.com/longbridge/gpui-component) (rev 38b3e6c9)

---

## 1. Basic Concepts

### Render Model

GPUI uses a **retained mode** approach with **immediate-mode-style APIs**:

- Views are structs that implement the `Render` trait
- The `render()` method returns a declarative element tree
- GPUI automatically tracks dependencies and re-renders when `cx.notify()` is called
- Element trees are diffed and only changed portions are repainted

### Core Architecture

```
Application
  └── Window(s)
        └── Root (gpui-component)
              └── Your View (implements Render)
                    └── Element tree (div, Button, Input, etc.)
```

---

## 2. Core Patterns

### Creating a View

```rust
use gpui::{div, prelude::*, Context, IntoElement, Render, Window};

pub struct MyView {
    count: usize,
}

impl Render for MyView {
    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
        div()
            .size_full()
            .flex()
            .items_center()
            .justify_center()
            .child(format!("Count: {}", self.count))
    }
}
```

### The Render Signature

```rust
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement
```

- `&mut self` - Mutable access to your view's state
- `window: &mut Window` - Access to window APIs (focus, dialogs, notifications)
- `cx: &mut Context<Self>` - Entity context for spawning tasks, subscriptions, notify

### Application Entry Point

```rust
use gpui::{Application, WindowOptions, px, size, Bounds, Point};
use gpui_component::Root;

fn main() {
    let app = Application::new();

    app.run(move |cx| {
        // Initialize gpui-component (required!)
        gpui_component::init(cx);

        let options = WindowOptions {
            window_bounds: Some(gpui::WindowBounds::Windowed(Bounds {
                origin: Point::default(),
                size: size(px(800.0), px(600.0)),
            })),
            titlebar: Some(gpui::TitlebarOptions {
                title: Some("My App".into()),
                ..Default::default()
            }),
            ..Default::default()
        };

        cx.open_window(options, |window, cx| {
            let view = cx.new(|_cx| MyView { count: 0 });
            // Wrap in Root for dialog/notification support
            cx.new(|cx| Root::new(view, window, cx))
        }).unwrap();
    });
}
```

### div() and Element Builders

`div()` is the fundamental building block:

```rust
div()
    // Layout
    .size_full()           // width: 100%, height: 100%
    .w(px(200.0))          // fixed width
    .h(px(100.0))          // fixed height
    .w_full()              // width: 100%
    .h_full()              // height: 100%
    .min_w(px(100.0))      // min-width
    .max_w(px(500.0))      // max-width

    // Flexbox
    .flex()                // display: flex
    .flex_col()            // flex-direction: column
    .flex_row()            // flex-direction: row (default)
    .flex_1()              // flex: 1
    .flex_grow()           // flex-grow: 1
    .flex_shrink_0()       // flex-shrink: 0
    .flex_wrap()           // flex-wrap: wrap

    // Alignment
    .items_center()        // align-items: center
    .items_start()         // align-items: flex-start
    .items_end()           // align-items: flex-end
    .justify_center()      // justify-content: center
    .justify_between()     // justify-content: space-between
    .justify_start()       // justify-content: flex-start
    .justify_end()         // justify-content: flex-end

    // Spacing
    .p(px(16.0))           // padding: 16px
    .px(px(8.0))           // padding-left/right: 8px
    .py(px(8.0))           // padding-top/bottom: 8px
    .pt(px(4.0))           // padding-top
    .pb(px(4.0))           // padding-bottom
    .pl(px(4.0))           // padding-left
    .pr(px(4.0))           // padding-right
    .m(px(8.0))            // margin: 8px
    .mt(px(8.0))           // margin-top
    .mb(px(8.0))           // margin-bottom
    .gap(px(8.0))          // gap between flex children
    .gap_2()               // gap: 0.5rem (Tailwind-style)

    // Colors
    .bg(gpui::rgb(0x1a1a1a))       // background color
    .text_color(gpui::rgb(0xffffff)) // text color
    .border_color(gpui::rgb(0x333333))

    // Borders
    .border_1()            // border-width: 1px
    .border_2()            // border-width: 2px
    .rounded(px(8.0))      // border-radius
    .rounded_md()          // medium border-radius
    .rounded_lg()          // large border-radius
    .rounded_full()        // fully rounded

    // Typography
    .text_sm()             // small text
    .text_base()           // base text
    .text_lg()             // large text
    .text_xl()             // extra large
    .text_2xl()            // 2x large
    .font_weight(gpui::FontWeight::BOLD)

    // Overflow
    .overflow_hidden()
    .overflow_scroll()

    // Children
    .child("Hello")
    .child(Button::new("btn").label("Click"))
    .children(items.iter().map(|i| div().child(i.name.clone())))
```

### px() for Pixels, rgb() for Colors

```rust
use gpui::{px, rgb, rgba, hsla, Hsla};

// Pixels
let width = px(200.0);
let padding = px(16.0);

// Colors
let black = rgb(0x000000);
let white = rgb(0xffffff);
let dark_bg = rgb(0x1a1a1a);
let semi_transparent = rgba(0x00000080); // with alpha

// HSLA colors
let custom = hsla(0.5, 0.8, 0.5, 1.0); // hue, saturation, lightness, alpha
```

### Parent/Child Relationships

```rust
div()
    .child("Single child")
    .child(
        div()
            .child("Nested")
            .child("Children")
    )
    .children(vec!["A", "B", "C"]) // multiple children from iterator
    .children(
        items.iter().map(|item| {
            div().child(item.name.clone())
        })
    )
```

---

## 3. gpui-component Widgets

### Setup

```rust
// In Cargo.toml
[dependencies]
gpui-component = { git = "https://github.com/longbridge/gpui-component", rev = "38b3e6c9" }

// In main.rs - MUST call before using components
gpui_component::init(cx);
```

### Button

```rust
use gpui_component::button::*;

// Basic button
Button::new("my-btn")
    .label("Click Me")
    .on_click(|_event, _window, _cx| {
        println!("Clicked!");
    })

// Variants
Button::new("primary").label("Primary").primary()
Button::new("danger").label("Delete").danger()
Button::new("warning").label("Warning").warning()
Button::new("success").label("Success").success()
Button::new("info").label("Info").info()
Button::new("ghost").label("Ghost").ghost()
Button::new("link").label("Link").link()
Button::new("text").label("Text").text()

// Outline style (border only)
Button::new("outlined").label("Outlined").primary().outline()

// With icon
use gpui_component::IconName;
Button::new("icon-btn")
    .icon(IconName::Plus)
    .label("Add")

// Icon only
Button::new("icon-only")
    .icon(IconName::Settings)

// States
Button::new("disabled").label("Disabled").disabled(true)
Button::new("loading").label("Loading...").loading(true)
Button::new("selected").label("Selected").selected(true)

// Sizes
Button::new("small").label("Small").with_size(Size::Small)
Button::new("large").label("Large").with_size(Size::Large)

// Compact (reduced padding)
Button::new("compact").label("Compact").compact()

// With tooltip
Button::new("tip").label("Hover me").tooltip("This is a tooltip")

// Click handler with cx.listener()
Button::new("save")
    .label("Save")
    .on_click(cx.listener(|this, _event, _window, _cx| {
        this.save_data();
    }))
```

### Input

```rust
use gpui_component::input::*;

// Create input state (store in your view struct)
pub struct MyView {
    input_state: Entity<InputState>,
    _subscriptions: Vec<Subscription>,
}

impl MyView {
    fn new(cx: &mut Context<Self>) -> Self {
        let input_state = cx.new(|cx| {
            InputState::new(cx)
                .placeholder("Enter text...")
        });

        // Subscribe to input changes
        let subscription = cx.subscribe(&input_state, |this, _state, event, cx| {
            match event {
                InputEvent::Change => {
                    // Get current value
                    let value = this.input_state.read(cx).text().to_string();
                    println!("Input changed: {}", value);
                }
                InputEvent::PressEnter { secondary } => {
                    println!("Enter pressed, secondary: {}", secondary);
                }
                InputEvent::Focus => println!("Focused"),
                InputEvent::Blur => println!("Blurred"),
            }
        });

        Self {
            input_state,
            _subscriptions: vec![subscription],
        }
    }
}

// Render the input
impl Render for MyView {
    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        div()
            .child(
                Input::new(&self.input_state)
                    .cleanable(true)        // Show clear button
                    .with_size(Size::Medium)
            )
    }
}

// Password input with toggle
Input::new(&self.password_state)
    .mask_toggle()  // Shows eye icon to toggle visibility

// With prefix/suffix
Input::new(&self.input_state)
    .prefix(IconName::Search)
    .suffix("@example.com")

// Disabled
Input::new(&self.input_state)
    .disabled(true)
```

### Modal/Dialog

```rust
use gpui_component::WindowExt;

// In your view
impl MyView {
    fn show_dialog(&mut self, _event: &ClickEvent, window: &mut Window, cx: &mut Context<Self>) {
        window.open_dialog(cx, |dialog, _window, _cx| {
            dialog
                .title("Confirm Action")
                .child("Are you sure you want to proceed?")
                .on_ok(|_window, _cx| {
                    println!("OK clicked");
                    true // Return true to close dialog
                })
                .on_cancel(|_window, _cx| {
                    println!("Cancel clicked");
                    true // Return true to close dialog
                })
        });
    }
}

// Dialog variants
// Confirm dialog (OK + Cancel buttons)
window.open_dialog(cx, |dialog, _, _| {
    dialog.title("Confirm").child("Continue?").confirm()
});

// Alert dialog (OK button only)
window.open_dialog(cx, |dialog, _, _| {
    dialog.title("Alert").child("Something happened!").alert()
});

// Custom dialog
window.open_dialog(cx, |dialog, _, _| {
    dialog
        .title("Custom Dialog")
        .width(px(600.0))
        .overlay(true)           // Show backdrop
        .overlay_closable(true)  // Click backdrop to close
        .keyboard(true)          // Escape to close
        .close_button(true)      // Show X button
        .child(
            div()
                .p_4()
                .child("Custom content here")
        )
});

// Close dialog programmatically
window.close_dialog(cx);
window.close_all_dialogs(cx);

// Check if dialog is open
if window.has_active_dialog(cx) { ... }

// IMPORTANT: Render dialog layer in your root view
fn render(...) -> impl IntoElement {
    div()
        .child(/* your content */)
        .children(Root::render_dialog_layer(window, cx))
}
```

### Sheet (Side Drawer)

```rust
use gpui_component::{WindowExt, Placement};

// Open sheet (default: right side)
window.open_sheet(cx, |sheet, _window, _cx| {
    sheet
        .title("Settings")
        .child(
            div().p_4().child("Sheet content")
        )
});

// Open at specific placement
window.open_sheet_at(Placement::Left, cx, |sheet, _, _| {
    sheet.title("Left Drawer").child("Content")
});

// Close sheet
window.close_sheet(cx);

// IMPORTANT: Render sheet layer
fn render(...) -> impl IntoElement {
    div()
        .child(/* your content */)
        .children(Root::render_sheet_layer(window, cx))
}
```

### Notification (Toast)

```rust
use gpui_component::{Notification, WindowExt};

// Push notification
window.push_notification(
    Notification::success("Operation completed!"),
    cx
);

// Notification types
Notification::info("Information message")
Notification::success("Success!")
Notification::warning("Warning: Check this")
Notification::error("Error occurred")

// With title
Notification::success("File saved successfully")
    .title("Save Complete")

// Custom ID (for replacing/removing specific notifications)
struct SaveNotification;
Notification::success("Saved")
    .id::<SaveNotification>()

// Disable auto-hide
Notification::info("Persistent message")
    .autohide(false)

// With action button
Notification::info("New update available")
    .action(|_window, _cx| {
        println!("Action clicked");
    })

// Remove specific notification
window.remove_notification::<SaveNotification>(cx);

// Clear all
window.clear_notifications(cx);
```

### Icon

```rust
use gpui_component::{Icon, IconName};

// Common icons
IconName::Check
IconName::Close
IconName::Plus
IconName::Minus
IconName::Search
IconName::Settings
IconName::Settings2
IconName::Info
IconName::TriangleAlert
IconName::CircleCheck
IconName::CircleX
IconName::ChevronDown
IconName::ChevronUp
IconName::ChevronLeft
IconName::ChevronRight
IconName::ArrowLeft
IconName::ArrowRight
IconName::ArrowUp
IconName::ArrowDown
IconName::Eye
IconName::EyeOff
IconName::Copy
IconName::Delete
IconName::File
IconName::Folder
IconName::FolderOpen
IconName::Loader
IconName::LoaderCircle
IconName::Menu

// Use in elements
div().child(Icon::new(IconName::Check).size(Size::Large))

// With custom color
Icon::new(IconName::Check).text_color(gpui::rgb(0x00ff00))

// In buttons
Button::new("add").icon(IconName::Plus).label("Add")
```

### h_flex, v_flex Layout Helpers

```rust
use gpui_component::{h_flex, v_flex};

// Horizontal flex (row, centered items)
h_flex()
    .gap_4()
    .child(Button::new("a").label("A"))
    .child(Button::new("b").label("B"))

// Vertical flex (column)
v_flex()
    .gap_2()
    .child("Item 1")
    .child("Item 2")
    .child("Item 3")
```

### TabBar and Tab

```rust
use gpui_component::tab::{Tab, TabBar};

pub struct MyView {
    selected_tab: usize,
}

impl Render for MyView {
    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        v_flex()
            .child(
                TabBar::new("main-tabs")
                    .selected_index(self.selected_tab)
                    .child(Tab::new().label("Dashboard").icon(IconName::Home))
                    .child(Tab::new().label("Settings").icon(IconName::Settings))
                    .child(Tab::new().label("Logs").icon(IconName::File))
                    .on_click(cx.listener(|this, index: &usize, _window, cx| {
                        this.selected_tab = *index;
                        cx.notify();
                    }))
            )
            .child(
                // Tab content
                match self.selected_tab {
                    0 => div().child("Dashboard content"),
                    1 => div().child("Settings content"),
                    2 => div().child("Logs content"),
                    _ => div(),
                }
            )
    }
}

// Tab variants
TabBar::new("tabs").pill()      // Pill style
TabBar::new("tabs").outline()   // Outlined
TabBar::new("tabs").segmented() // Segmented control
TabBar::new("tabs").underline() // Underlined
```

### Checkbox

```rust
use gpui_component::Checkbox;

pub struct MyView {
    is_checked: bool,
}

// In render
Checkbox::new("my-checkbox")
    .label("Enable feature")
    .checked(self.is_checked)
    .on_click(cx.listener(|this, checked: &bool, _window, cx| {
        this.is_checked = *checked;
        cx.notify();
    }))
```

### Switch

```rust
use gpui_component::Switch;

Switch::new("my-switch")
    .label("Dark mode")
    .checked(self.dark_mode)
    .on_click(cx.listener(|this, checked: &bool, _window, cx| {
        this.dark_mode = *checked;
        cx.notify();
    }))
```

### Progress

```rust
use gpui_component::Progress;

Progress::new("download-progress")
    .value(75.0)  // 0.0 to 100.0
    .color(gpui::rgb(0x22c55e))  // Custom color
    .with_size(Size::Small)
```

### Spinner

```rust
use gpui_component::Spinner;

Spinner::new()
    .with_size(Size::Medium)
    .color(gpui::rgb(0x3b82f6))
```

### Alert

```rust
use gpui_component::Alert;

Alert::info("info-alert", "This is informational")
Alert::success("success-alert", "Operation successful!")
Alert::warning("warning-alert", "Please be careful")
Alert::error("error-alert", "Something went wrong")

// With title
Alert::info("alert", "Details here")
    .title("Notice")

// Closable
Alert::warning("closable", "Can be dismissed")
    .on_close(|_window, _cx| {
        println!("Alert closed");
    })

// Banner style (full width)
Alert::info("banner", "System maintenance scheduled")
    .banner()
```

### Label

```rust
use gpui_component::Label;

Label::new("Main text")
    .secondary("(optional)")  // Muted secondary text

// With highlights
Label::new("Search results")
    .highlights("results")  // Highlights matching text
```

### TitleBar

```rust
use gpui_component::TitleBar;

// Custom title bar (for frameless windows)
div()
    .child(
        TitleBar::new()
            .child("My Application")
    )
    .child(/* rest of content */)

// Window options for custom title bar
let options = WindowOptions {
    titlebar: Some(TitleBar::title_bar_options()),
    ..Default::default()
};
```

---

## 4. Common Use Cases

### Text Input with Validation

```rust
pub struct LoginView {
    email_input: Entity<InputState>,
    password_input: Entity<InputState>,
    email_error: Option<String>,
    _subscriptions: Vec<Subscription>,
}

impl LoginView {
    fn new(cx: &mut Context<Self>) -> Self {
        let email_input = cx.new(|cx| InputState::new(cx).placeholder("Email"));
        let password_input = cx.new(|cx| InputState::new(cx).placeholder("Password"));

        let email_sub = cx.subscribe(&email_input, |this, _state, event, cx| {
            if matches!(event, InputEvent::Change) {
                this.validate_email(cx);
            }
        });

        Self {
            email_input,
            password_input,
            email_error: None,
            _subscriptions: vec![email_sub],
        }
    }

    fn validate_email(&mut self, cx: &mut Context<Self>) {
        let email = self.email_input.read(cx).text().to_string();
        self.email_error = if email.contains('@') {
            None
        } else {
            Some("Invalid email".to_string())
        };
        cx.notify();
    }
}

impl Render for LoginView {
    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        v_flex()
            .gap_4()
            .p_4()
            .child(
                v_flex()
                    .gap_1()
                    .child(Input::new(&self.email_input))
                    .when_some(self.email_error.clone(), |this, error| {
                        this.child(
                            div()
                                .text_sm()
                                .text_color(gpui::rgb(0xef4444))
                                .child(error)
                        )
                    })
            )
            .child(
                Input::new(&self.password_input)
                    .mask_toggle()
            )
    }
}
```

### Button with Loading State

```rust
pub struct SubmitView {
    is_loading: bool,
}

impl Render for SubmitView {
    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        Button::new("submit")
            .label(if self.is_loading { "Saving..." } else { "Save" })
            .loading(self.is_loading)
            .disabled(self.is_loading)
            .on_click(cx.listener(|this, _, _, cx| {
                this.is_loading = true;
                cx.notify();

                // Spawn async operation
                cx.spawn(async move |this, mut cx| {
                    // Simulate API call
                    Timer::after(Duration::from_secs(2)).await;

                    this.update(&mut cx, |this, cx| {
                        this.is_loading = false;
                        cx.notify();
                    }).ok();
                }).detach();
            }))
    }
}
```

### Tab Navigation

```rust
pub struct AppView {
    active_tab: usize,
}

impl Render for AppView {
    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        v_flex()
            .size_full()
            .child(
                TabBar::new("nav")
                    .selected_index(self.active_tab)
                    .child(Tab::new().label("Home").icon(IconName::Home))
                    .child(Tab::new().label("Profile").icon(IconName::User))
                    .child(Tab::new().label("Settings").icon(IconName::Settings))
                    .on_click(cx.listener(|this, index: &usize, _, cx| {
                        this.active_tab = *index;
                        cx.notify();
                    }))
            )
            .child(
                div()
                    .flex_1()
                    .p_4()
                    .child(match self.active_tab {
                        0 => "Home content",
                        1 => "Profile content",
                        2 => "Settings content",
                        _ => "",
                    })
            )
    }
}
```

### Modal Dialogs

```rust
impl MyView {
    fn confirm_delete(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context<Self>) {
        window.open_dialog(cx, |dialog, _, _| {
            dialog
                .title("Delete Item")
                .child("Are you sure you want to delete this item? This cannot be undone.")
                .confirm()
                .on_ok(|window, cx| {
                    // Perform delete
                    window.push_notification(
                        Notification::success("Item deleted"),
                        cx
                    );
                    true
                })
        });
    }
}
```

### Conditional Rendering with .when()

```rust
use gpui::FluentBuilder;  // Import the trait

div()
    .child("Always visible")
    // Conditional child
    .when(self.show_extra, |this| {
        this.child("Extra content")
    })
    // Conditional styling
    .when(self.is_active, |this| {
        this.bg(gpui::rgb(0x3b82f6))
    })
    // When with else
    .when_else(
        self.is_loading,
        |this| this.child(Spinner::new()),
        |this| this.child("Content loaded")
    )
    // When some Option is present
    .when_some(self.error_message.clone(), |this, error| {
        this.child(Alert::error("err", error))
    })
    // When Option is None
    .when_none(&self.data, |this| {
        this.child("No data available")
    })
```

### Event Handling with cx.listener()

```rust
// cx.listener() captures `self` and provides access in the callback
Button::new("btn")
    .on_click(cx.listener(|this, event, window, cx| {
        // `this` is &mut Self
        // `event` is &ClickEvent
        // `window` is &mut Window
        // `cx` is &mut Context<Self>

        this.handle_click();
        cx.notify();  // Trigger re-render
    }))

// For keyboard events
div()
    .on_key_down(cx.listener(|this, event, window, cx| {
        if event.keystroke.key == "Enter" {
            this.submit();
        }
    }))
```

### Async Operations with cx.spawn()

```rust
impl MyView {
    fn fetch_data(&mut self, cx: &mut Context<Self>) {
        self.is_loading = true;
        cx.notify();

        cx.spawn(async move |this, mut cx| {
            // Async work (runs on foreground thread)
            let result = fetch_from_api().await;

            // Update state back on main thread
            this.update(&mut cx, |this, cx| {
                this.is_loading = false;
                this.data = Some(result);
                cx.notify();
            }).ok();
        }).detach();
    }
}
```

### Periodic Polling/Updates

```rust
use gpui::Timer;
use std::time::Duration;

impl MyView {
    fn start_polling(&mut self, cx: &mut Context<Self>) {
        cx.spawn(async move |this, mut cx| {
            loop {
                // Wait for interval
                Timer::after(Duration::from_secs(5)).await;

                // Update state
                let result = this.update(&mut cx, |this, cx| {
                    this.refresh_data();
                    cx.notify();
                });

                // Stop if view was dropped
                if result.is_err() {
                    break;
                }
            }
        }).detach();
    }
}
```

### Subscriptions for Entity Events

```rust
pub struct ParentView {
    child: Entity<ChildView>,
    _subscriptions: Vec<Subscription>,
}

impl ParentView {
    fn new(cx: &mut Context<Self>) -> Self {
        let child = cx.new(|_| ChildView::new());

        // Subscribe to events from child
        let sub = cx.subscribe(&child, |this, _child, event: &ChildEvent, cx| {
            match event {
                ChildEvent::Selected(item) => {
                    this.handle_selection(item);
                    cx.notify();
                }
            }
        });

        Self {
            child,
            _subscriptions: vec![sub],
        }
    }
}

// Child emits events
pub struct ChildView;

pub enum ChildEvent {
    Selected(String),
}

impl EventEmitter<ChildEvent> for ChildView {}

impl ChildView {
    fn select(&mut self, item: String, cx: &mut Context<Self>) {
        cx.emit(ChildEvent::Selected(item));
    }
}
```

---

## 5. Theming

### Accessing Theme Colors

```rust
use gpui_component::ActiveTheme;

impl Render for MyView {
    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        let theme = cx.theme();

        div()
            .bg(theme.background)
            .text_color(theme.foreground)
            .border_color(theme.border)
    }
}
```

### Theme Color Properties

The theme provides these semantic colors:

```rust
let theme = cx.theme();

// Backgrounds
theme.background       // Main background
theme.foreground       // Main text color
theme.muted            // Muted/secondary background
theme.muted_foreground // Muted text

// Interactive states
theme.primary          // Primary action color
theme.primary_hover    // Primary hover state
theme.secondary        // Secondary background
theme.secondary_hover  // Secondary hover

// Semantic colors
theme.destructive      // Error/danger actions
theme.success          // Success states
theme.warning          // Warning states

// Borders
theme.border           // Default border color
theme.ring             // Focus ring color
```

### Theme Mode (Light/Dark)

```rust
use gpui_component::theme::{Theme, ThemeMode};

// Set theme mode
Theme::change(ThemeMode::Dark, cx);
Theme::change(ThemeMode::Light, cx);

// Sync with system
Theme::sync_system_appearance(None, cx);
```

### Custom Colors with Root

```rust
// Set background on Root
cx.new(|cx| {
    Root::new(view, window, cx)
        .bg(cx.theme().background)
})
```

---

## 6. Quick Reference

### Common Imports

```rust
use gpui::{
    div, prelude::*, px, rgb, rgba, hsla,
    Application, Context, FocusHandle, IntoElement,
    ParentElement, Render, Styled, Window, WindowOptions,
    FluentBuilder,  // For .when()
};

use gpui_component::{
    // Core
    Root, h_flex, v_flex, ActiveTheme,

    // Components
    button::*, Button,
    input::{Input, InputState, InputEvent},
    Icon, IconName,
    Notification,
    Spinner,
    Progress,
    Alert,
    Label,
    Checkbox,
    Switch,
    TitleBar,
    tab::{Tab, TabBar},

    // Traits
    WindowExt,  // For dialogs, sheets, notifications
    Sizable,    // For .with_size()
    Size,       // Size::Small, Size::Medium, etc.
};
```

### Size Enum

```rust
use gpui_component::Size;

Size::XSmall
Size::Small
Size::Medium  // Default
Size::Large

// Usage
Button::new("btn").with_size(Size::Small)
Input::new(&state).with_size(Size::Large)
```

### Element ID Patterns

```rust
// String IDs
Button::new("save-button")
TabBar::new("main-tabs")

// Dynamic IDs
Button::new(format!("item-{}", item.id))

// Entity IDs (for unique identification)
div().id(ElementId::new("unique-id"))
```

### Style Method Chaining Order

Recommended order for readability:

```rust
div()
    // 1. Size & position
    .size_full()
    .w(px(200.0))

    // 2. Display & flex
    .flex()
    .flex_col()
    .items_center()
    .justify_center()
    .gap_4()

    // 3. Spacing
    .p_4()
    .m_2()

    // 4. Colors & borders
    .bg(color)
    .text_color(color)
    .border_1()
    .border_color(color)
    .rounded_lg()

    // 5. Typography
    .text_lg()
    .font_weight(FontWeight::BOLD)

    // 6. Interactions
    .cursor_pointer()
    .hover(|s| s.bg(hover_color))

    // 7. Focus
    .track_focus(&self.focus_handle)

    // 8. Children
    .child(content)
    .children(items)
```

---

## 7. Troubleshooting

### Common Issues

**Dialog/Sheet not showing:**

- Ensure you render the dialog/sheet layers:

```rust
div()
    .child(/* content */)
    .children(Root::render_dialog_layer(window, cx))
    .children(Root::render_sheet_layer(window, cx))
```

**Input not updating:**

- Ensure you store `_subscriptions` to keep them alive
- Call `cx.notify()` after state changes

**View not re-rendering:**

- Call `cx.notify()` when state changes
- Ensure you're mutating state, not creating copies

**Async task not updating UI:**

- Use `this.update(&mut cx, ...)` to modify state from async context
- Call `cx.notify()` inside the update closure

**Focus not working:**

- Store `FocusHandle` in your view struct
- Use `.track_focus(&self.focus_handle)` on your root element