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.
Install command
npx @skill-hub/cli install legacy3-wowlab-gpui
Repository
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 repositoryBest 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
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