zed-industries/zed
GPUI
Active contributors: mikayla-maki, Anthony-Eid, cameron1024, probably-neb
Purpose
GPUI is Zed's homegrown UI framework. It provides windowing, GPU-accelerated rendering, input handling, layout (flexbox-style), an entity/state model, and an async runtime built around a single foreground thread. It is the substrate every other UI crate sits on.
Directory layout
crates/
├── gpui/ # The framework. ~62k LOC
│ ├── docs/ # Internal docs
│ ├── examples/ # Self-contained example apps
│ ├── resources/ # Shaders, fonts, default assets
│ └── src/ # Library code
├── gpui_platform/ # Cross-platform window/event abstraction
├── gpui_wgpu/ # WGPU-based renderer (cross-platform)
├── gpui_macos/ # macOS native window glue
├── gpui_linux/ # X11 / Wayland window glue
├── gpui_windows/ # Win32 window glue
├── gpui_web/ # Web target (experimental)
├── gpui_macros/ # Procedural macros (e.g. #[derive(IntoElement)])
├── gpui_shared_string/ # The SharedString type (`Arc<str>` or `&'static str`)
├── gpui_tokio/ # Tokio bridge (for crates that need a Tokio runtime)
├── gpui_util/ # Misc helpers (collections, channels)Key abstractions
| Type | Purpose |
|---|---|
Application |
The OS-level app singleton. Created in main |
App |
Root context. Globals, entity registry, system services |
Window |
OS-level window. Focus, dispatch, draw |
Entity<T> |
Reference-counted handle to state |
WeakEntity<T> |
Weak handle (returns Result on access) |
Context<T> |
Mutating context for an Entity<T>. Derefs to App |
AsyncApp, AsyncWindowContext |
Contexts that survive .await |
Element / IntoElement |
The render-tree primitives |
Render / RenderOnce |
Traits implemented by views and stateless components |
Task<R> |
A future returned by cx.spawn or cx.background_spawn |
SharedString |
Arc<str> or &'static str — used everywhere strings are displayed |
actions! / Action |
Macro and derive for typed actions |
EventEmitter<E> |
Marker trait for entities that emit events |
Subscription |
RAII handle returned by observe/subscribe |
FocusHandle |
Routes keyboard focus + actions |
How it works
The single-thread invariant
All entity updates, observations, events, and rendering happen on one foreground thread. The type system enforces this: App and Context<T> are not Send. To do work elsewhere you cross into a Task via cx.spawn (foreground async) or cx.background_spawn (threadpool).
graph LR
main[Foreground thread] -->|cx.spawn| async[AsyncApp / future]
main -->|cx.background_spawn| bg[Background thread pool]
bg -->|Task::ready / await| async
async -->|update entity| mainEntity update pattern
let counter = cx.new(|_cx| Counter { value: 0 });
counter.update(cx, |this, cx| {
this.value += 1;
cx.notify(); // re-render any view of this entity
cx.emit(Bumped); // notify subscribers
});Inside the closure you must use the inner cx — capturing the outer one breaks borrow rules and the inner one is what's actually live.
Render trait
struct Greeting(SharedString);
impl Render for Greeting {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
div().text_lg().child(self.0.clone())
}
}For stateless components, prefer RenderOnce plus #[derive(IntoElement)]. The style methods are Tailwind-flavored (.px_2(), .bg(color), .rounded_md()).
Subscriptions and observers
let _sub = cx.observe(&other, |this, other, cx| {
/* called whenever other.notify() fires */
});
let _sub = cx.subscribe(&other, |this, other, event: &SomeEvent, cx| {
/* called when other emits SomeEvent */
});Subscription handles deregister on drop. Typically held in a _subscriptions: Vec<Subscription> field.
Actions
actions!(my_panel, [Reload, ClearAll]);
div().on_action(cx.listener(|this, _: &Reload, window, cx| {
this.reload(cx);
}))For data-bearing actions, use #[derive(Action)]. Doc comments become user-visible help.
Tasks
let task = cx.spawn(async move |cx| {
let data = fetch().await?;
cx.update(|cx| {
// update entities, render, etc.
})
});
task.detach_and_log_err(cx); // OR await it OR store itA dropped Task is cancelled. Unless you intend cancellation, detach, await, or store it.
Integration points
- Used by: every crate that touches the UI. The
editor,agent_ui,workspace, anduicrates are the heaviest users. - Backed by:
gpui_wgpufor rendering,gpui_macos/linux/windowsfor windowing. - Bridges:
gpui_tokiolets Tokio-only libraries run alongside the GPUI executor.
Entry points for modification
- New element type — implement
Element(low-level) orRenderOnce(high-level). - New global — register in the GPUI app via
cx.set_global(value). - New input gesture — extend
Windowevent handling incrates/gpui/src/window.rs. - Per-platform windowing —
gpui_{macos,linux,windows}/src.
Related pages
- Patterns and conventions — GPUI usage rules
- Editor — the largest GPUI consumer
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.