Getting Started
GPUI-RSX is a procedural macro for writing GPUI views with JSX-like syntax. Static markup expands to normal GPUI builder calls during compilation, so the generated UI remains ordinary Rust code.
Install
Section titled “Install”Add GPUI from the Zed repository and GPUI-RSX to your application:
[dependencies]gpui = { git = "https://github.com/zed-industries/zed" }gpui_platform = { git = "https://github.com/zed-industries/zed", features = ["font-kit", "runtime_shaders", "wayland", "x11"] }gpui-rsx = "0.6"Applications should commit Cargo.lock so the exact GPUI git revision is pinned.
If you are upgrading from the crates.io gpui = "0.2.2" package, read the migration guide before changing dependencies.
Imports
Section titled “Imports”use gpui::*;use gpui::prelude::*;use gpui_platform::application;use gpui_rsx::rsx;First Element
Section titled “First Element”The smallest RSX expression creates a GPUI div():
rsx! { <div>{"Hello, GPUI!"}</div>}String children can be written as literals or Rust expressions:
rsx! { <div> "Count: " {self.count.to_string()} </div>}Attributes
Section titled “Attributes”Flag attributes become zero-argument method calls:
rsx! { <div flex flex_col rounded_md />}Value attributes pass Rust expressions to builder methods:
rsx! { <div gap={px(16.0)} bg={rgb(0x3b82f6)} />}CamelCase aliases are mapped where GPUI uses snake_case:
rsx! { <button onClick={handler} textColor={rgb(0xffffff)}> {"Save"} </button>}Class Attribute
Section titled “Class Attribute”Use class for stable Tailwind-like utility lists:
rsx! { <div class="flex flex-col gap-4 p-4 bg-blue-500 text-white"> {"Content"} </div>}Static class strings are parsed by the macro. Dynamic strings are handled by a generated runtime matcher and are useful for values assembled at runtime:
let row_class = if selected { "bg-blue-500 text-white" } else { "bg-gray-100" };
rsx! { <div class={row_class}>{label.as_str()}</div>}Prefer direct attributes when the value is already a Rust expression:
rsx! { <div class="flex" w={px(self.sidebar_width)} />}Events
Section titled “Events”Event attributes accept normal GPUI listeners:
rsx! { <button class="px-4 py-2 bg-blue-500 text-white rounded-md cursor-pointer" onClick={cx.listener(|view, _, _window, cx| { view.count += 1; cx.notify(); })} > {"Increment"} </button>}For repeated stateful elements inside {for ...}, add key={...} so each iteration gets a unique identity:
rsx! { <ul> {for task in &self.tasks { <li key={task.id} onClick={handler}> {task.title.as_str()} </li> }} </ul>}Complete View
Section titled “Complete View”use gpui::*;use gpui::prelude::*;use gpui_platform::application;use gpui_rsx::rsx;
struct HelloView;
impl Render for HelloView { fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement { rsx! { <div class="flex flex-col gap-3 p-4"> <h1 class="text-xl font-bold">{"Hello GPUI-RSX"}</h1> <p class="text-gray-600">{"This markup expands to GPUI builder calls."}</p> </div> } }}
fn main() { application().run(|cx: &mut App| { cx.open_window(WindowOptions::default(), |_, cx| { cx.new(|_cx| HelloView) }).unwrap(); cx.activate(true); });}Check the Demo
Section titled “Check the Demo”The repository includes a demo crate that exercises real GPUI integration:
cargo check --manifest-path demo/Cargo.toml --bins --lockedThe demo lockfile pins GPUI and gpui-component so compatibility problems are reproducible.
Next Steps
Section titled “Next Steps”- Read Syntax Reference for elements, attributes, children, and fragments.
- Read Class Handling before relying on dynamic class strings.
- Read IDs and Keys before rendering interactive rows in loops.