--- name: gpui description: GPUI UI framework best practices for building desktop applications. Use when writing GPUI code, creating UI components, handling state/events, async tasks, animations, lists, forms, testing, or working with Zed-style Rust GUI code. license: MIT metadata: author: AprilNEA version: "1.0.0" --- # GPUI Best Practices Comprehensive guide for building desktop applications with GPUI, the UI framework powering Zed editor. Contains 40+ rules across 8 categories, prioritized by impact. ## When to Apply Reference these guidelines when: - Writing new GPUI views or components - Implementing state management with Entity - Handling events and keyboard shortcuts - Working with async tasks and background work - Building forms, lists, or dialogs - Styling components with Tailwind-like API - Testing GPUI applications ## Rule Categories by Priority | Priority | Category | Impact | Prefix | |----------|----------|--------|--------| | 1 | Core Concepts | CRITICAL | `core-` | | 2 | Rendering | CRITICAL | `render-` | | 3 | State Management | HIGH | `state-` | | 4 | Event Handling | HIGH | `event-` | | 5 | Async & Concurrency | MEDIUM-HIGH | `async-` | | 6 | Styling | MEDIUM | `style-` | | 7 | Components | MEDIUM | `comp-` | | 8 | Anti-patterns | CRITICAL | `anti-` | ## Quick Reference ### 1. Core Concepts (CRITICAL) - `core-ownership-model` - Understand GPUI's single ownership model - `core-entity-operations` - Use read/update/observe/subscribe correctly - `core-weak-entity` - Use WeakEntity to break circular references - `core-context-types` - Know when to use App, Context, AsyncApp ### 2. Rendering (CRITICAL) - `render-render-vs-renderonce` - Choose Render for stateful, RenderOnce for components - `render-element-composition` - Build element trees with div() and method chaining - `render-conditional` - Use .when() and .when_some() for conditional styling - `render-shared-string` - Use SharedString to avoid string copying - `render-builder-pattern` - Design components with builder pattern ### 3. State Management (HIGH) - `state-notify` - Always call cx.notify() after state changes - `state-observe` - Use cx.observe() to react to Entity changes - `state-subscribe` - Use cx.subscribe() for typed events - `state-global` - Use Global trait for app-wide state - `state-keyed-state` - Use window.use_keyed_state() for persistent state ### 4. Event Handling (HIGH) - `event-actions` - Define and register actions for keyboard shortcuts - `event-listener` - Use cx.listener() for view-bound event handlers - `event-focus` - Manage focus with FocusHandle and key_context - `event-propagation` - Understand event bubbling and stop_propagation ### 5. Async & Concurrency (MEDIUM-HIGH) - `async-task-lifecycle` - Store or detach tasks to prevent cancellation - `async-debounce` - Implement debounce with timer + task replacement - `async-background-spawn` - Use background_spawn for CPU-intensive work - `async-weak-entity` - Use WeakEntity for safe cross-await access - `async-error-handling` - Use .log_err() and .detach_and_log_err() ### 6. Styling (MEDIUM) - `style-flexbox` - Use h_flex() and v_flex() for layouts - `style-theme-colors` - Always use cx.theme() for colors - `style-spacing` - Use DynamicSpacing for responsive spacing - `style-elevation` - Use elevation system for layered surfaces ### 7. Components (MEDIUM) - `comp-stateless` - Prefer RenderOnce with #[derive(IntoElement)] - `comp-traits` - Implement Disableable, Selectable, Sizable traits - `comp-focus-ring` - Add focus ring for accessibility - `comp-dialog` - Use WindowExt for dialog management - `comp-variant` - Use variant enums for component styles ### 8. Anti-patterns (CRITICAL) - `anti-silent-error` - Never silently discard errors with let _ = - `anti-drop-task` - Never drop Task without storing or detaching - `anti-drop-subscription` - Always detach or store subscriptions - `anti-circular-reference` - Avoid Entity cycles, use WeakEntity - `anti-missing-notify` - Never forget cx.notify() after state changes - `anti-unwrap` - Avoid unwrap(), use ? or explicit handling ## Architecture Overview ``` ┌─────────────────────────────────────────────────────────┐ │ Application (App) │ │ (Single owner of all Entities) │ └─────────────────────────────────────────────────────────┘ │ ┌───────────────────┼───────────────────┐ │ │ │ ┌───────────┐ ┌───────────┐ ┌──────────┐ │ Entity │ │ Entity │ │ Global│ └───────────┘ └───────────┘ └──────────┘ │ │ │ │ read/update │ read/update │ via App │ via Context │ via Context │ │ ┌─────────────────────────────────────────────────┐ │ UI Rendering (Render trait) │ │ Each frame: fn render(&mut self, ...) │ │ Returns: impl IntoElement (Element tree) │ └─────────────────────────────────────────────────┘ │ ├─ observe() → changes trigger render ├─ subscribe() → events trigger reactions ├─ notify() → signal changes └─ emit() → send typed events ``` ## How to Use Read individual rule files for detailed explanations and code examples: ``` rules/core-ownership-model.md rules/render-render-vs-renderonce.md rules/anti-silent-error.md ``` Each rule file contains: - Brief explanation of why it matters - Incorrect code example with explanation - Correct code example with explanation - Additional context and references