Components

GPUI patterns and primitives. Note: there is no button(),span(), or h1() element function — everything is built with div() and style modifiers. The ui crate provides higher-level components (tabs, modals, sliders).

Core Elements

div()

The fundamental building block. All UI elements are divs with style modifiers. Text is added via .child().

rust
use gpui::*; div() .flex() .flex_col() .size_full() .p_4() .gap_2() .bg(rgb(0x0c0c0c)) .text_color(rgb(0xffffff)) .child("Hello") .child( div() .id("child-el") .px_3() .py_2() .rounded_lg() .bg(rgb(0x0ea5e9)) .on_click(cx.listener(|this, _event, _window, cx| { cx.notify(); })) .child("Click me") )

Text

Text is rendered by passing a string to .child(). There is no span() or p() element.

rust
use gpui::*; div() .child("Plain text") .child( div() .text_size(px(24.0)) .font_weight(FontWeight::BOLD) .text_color(rgb(0x0ea5e9)) .child("Styled text") ) .child( div() .text_xs() .text_color(rgb(0x888888)) .child(format!("Dynamic: {}", count)) )

Interactivity

Click & Hover

Interactive elements need .id() for click events to work. Hover uses the .hover() style modifier.

rust
use gpui::*; div() .id("btn") .px_4() .py_2() .rounded_lg() .bg(rgb(0x0ea5e9)) .hover(|s| s.bg(rgb(0x0284c7))) .on_click(cx.listener(|this, _event, _window, cx| { // 4 listener args: (this, event, window, cx) this.count += 1; cx.notify(); })) .child("Click me")

Mouse Events

Low-level mouse events: on_mouse_down, on_mouse_up, on_mouse_move.

rust
use gpui::*; div() .id("mouse-area") .w(px(200.0)) .h(px(100.0)) .bg(rgb(0x333333)) .on_mouse_down(MouseButton::Left, cx.listener(|this, _event, _window, cx| { this.pressed = true; cx.notify(); })) .on_mouse_up(MouseButton::Left, cx.listener(|this, _event, _window, cx| { this.pressed = false; cx.notify(); }))

Layout

Flex

GPUI uses flexbox via style shortcuts. flex_1() sets flex-grow:1. overflow_y_scroll() enables scrolling.

rust
use gpui::*; div() .flex() .flex_col() .size_full() // Scrollable area .child( div() .id("scroll") .flex_1() .overflow_y_scroll() .gap_2() .children((0..20).map(|i| { div().p_2().bg(rgb(0x1a1a1a)).child(format!("Item {i}")) })) ) // Fixed bottom bar .child( div() .h(px(48.0)) .bg(rgb(0x0ea5e9)) .child("Footer") )

Positioning

Elements can be positioned with .absolute(), .relative(), .top(), .left(), etc.

rust
use gpui::*; div() .relative() .size_full() .child( div() .absolute() .top(px(16.0)) .right(px(16.0)) .bg(rgb(0xe94560)) .size_12() .rounded_full() ) .child( div() .absolute() .bottom(px(16.0)) .left(px(16.0)) .child("Bottom left") )

Common Patterns

Conditional Rendering

Use .when() and .when_some() for conditional children.

rust
use gpui::*; div() .when(is_loading, |el| el.child("Loading...")) .when(error.is_some(), |el| el.child("Error!")) .when_some(optional_value.as_ref(), |el, val| { el.child(format!("Value: {val}")) }) .children(items.iter().map(|item| { div().child(item.label.clone()) }))

cx.notify() pattern

On WASM, call cx.notify() at the start of render() to keep the frame loop running.

rust
use gpui::*; impl Render for MyView { fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { cx.notify(); // required on WASM for continuous rendering div().size_full().child(self.text.clone()) } }