home / skills / aprilnea / gpui-skills / gpui

gpui skill

/gpui

This skill helps you apply GPUI best practices to desktop UI development, improving performance, reliability, and maintainability across views, state, events,

npx playbooks add skill aprilnea/gpui-skills --skill gpui

Review the files below or copy the command above to add this skill to your agents.

Files (35)
SKILL.md
6.3 KB
---
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<T>, 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<A> │       │ Entity<B> │       │ Global<C>│
    └───────────┘       └───────────┘       └──────────┘
        │                   │                   │
        │ read/update       │ read/update       │ via App
        │ via Context<A>    │ via Context<B>    │
        │
    ┌─────────────────────────────────────────────────┐
    │           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

Overview

This skill captures GPUI UI framework best practices for building robust desktop applications. It condenses 40+ prioritized rules across core concepts, rendering, state, events, async, styling, components, and anti-patterns into a practical reference. Use it to speed development, avoid common pitfalls, and ensure consistent, maintainable GPUI code.

How this skill works

The skill inspects common GPUI patterns and prescribes clear, actionable rules for component design, state management, event handling, async tasks, styling, and testing. Each rule highlights the problem, shows incorrect and correct approaches, and recommends concrete APIs (Entity, Context, Render/RenderOnce, WeakEntity, background_spawn, cx.notify(), etc.). Follow the rules while writing views, components, or background tasks to reduce bugs and improve UX.

When to use it

  • Writing new GPUI views or composing elements with div(), h_flex(), v_flex()
  • Implementing state with Entity, Global, or keyed window state
  • Handling keyboard shortcuts, focus, or bubbling event propagation
  • Managing async work, debounced tasks, or CPU-bound background jobs
  • Building forms, lists, dialogs, animated components, or focusable controls
  • Writing tests and avoiding runtime issues like dropped tasks or silent errors

Best practices

  • Respect GPUI ownership: App is the single owner; use Context<T> and Entity APIs correctly
  • Always call cx.notify() after mutating state and prefer cx.observe()/subscribe() for reactions
  • Choose Render for stateful components and RenderOnce (IntoElement) for simple stateless widgets
  • Use WeakEntity to break reference cycles and to safely access Entities across await points
  • Never drop Task or Subscription without storing or detaching; log or propagate errors instead of silencing them
  • Use theme, spacing, and elevation helpers for consistent styling; prefer traits for reusable component behavior

Example use cases

  • A settings panel using Entity-backed state, keyed window state, and cx.observe() to update live previews
  • A file list with virtualization, builder-pattern components, and conditional rendering via .when()
  • A command palette with registered actions, keyboard shortcuts, FocusHandle, and proper event.stop_propagation()
  • Offloading syntax parsing to background_spawn with WeakEntity access and detach_and_log_err() for resilience
  • A dialog system using WindowExt, variant enums for styles, and focus rings for accessibility

FAQ

When should I use Render vs RenderOnce?

Use Render for components that need internal state, observe Entities, or re-render frequently. Use RenderOnce (IntoElement) for stateless, composition-only widgets to reduce overhead.

How do I avoid dropped background work?

Store tasks on the owning Entity or explicitly detach them. Use background_spawn for CPU-intensive jobs and WeakEntity to access state safely across awaits.