A Mantine-inspired component library for gpui — the GPU-accelerated Rust UI framework that powers Zed.
guise brings Mantine's ergonomics to gpui: a themed palette, sizing tokens,
and composable components built on gpui's RenderOnce builder pattern.
use guise::prelude::*;
Stack::new()
.gap(Size::Md)
.child(Title::new("Welcome").order(1))
.child(
Button::new("save", "Save changes")
.variant(Variant::Filled)
.color(ColorName::Blue)
.on_click(|_, window, cx| { /* ... */ }),
)Full docs live in docs/:
- Tutorial — build a complete app step by step (web version)
- Getting started · Theming · Component model
- Components: Buttons · Icons · Inputs · Typography · Layout · Panels · Feedback · Data · Charts · Editor · Overlays · Navigation
- Systems: Flex layout · Macros · Transitions · Reactive state · Window menu · Architecture
crates/guise— the component library.crates/gallery— a live showcase of every component (the Mantine-docs equivalent). Run it withcargo run -p gallery.
Install a theme once at startup; every component reads it from the gpui global:
guise::Theme::dark().init(cx); // or Theme::light()The theme carries the full Mantine / open-color palette (14 colors × 10 shades),
xs..xl scales for spacing, radius and font size, and scheme-aware semantic
colors (body, surface, text, dimmed, border).
Write colors the CSS way — hex, rgb/rgba, hsl/hsla, or named — with the
color! macro (compile-time) or css(..) (runtime strings). Both produce a
gpui Hsla, usable in .bg(..)/.text_color(..), in any component .color(..),
and in the theme with_* overrides:
Button::new("go", "Go").color(color!(rgba(112, 72, 232, 1)))
Badge::new("New").color(color!("#e64980"))
Theme::dark()
.with_primary(color!("#7048e8"))
.with_body(color!(rgb(11, 11, 15)))
.with_text(color!("hsl(220, 15%, 92%)"))
.init(cx); // restyles the whole UIcolor! takes rgb(..)/rgba(..)/hsl(..)/hsla(..)/named tokens or a CSS
string (hex must be a string — #228be6 isn't a Rust token). Component
.color(..) accepts a palette ColorName or an explicit color (the
ColorValue type) and derives variant shades from a single custom color. See
Theming.
| Group | Components |
|---|---|
| Layout | Stack, Group, Center, SimpleGrid, ScrollArea, AppShell, Container, Space, Panel, SplitPanel |
| Surface | Paper, Card |
| Typography | Text, Title, Mark, Blockquote, Spoiler |
| Inputs | Button, TextInput, TextArea, NumberInput, PasswordInput, PinInput, Checkbox, Switch, Radio, RadioGroup, CheckboxGroup, Select, Combobox, Slider, RangeSlider, Rating, ColorInput, TagsInput, Field |
| Editor | Editor (syntax highlighting: Language::Rust / Sql / Json) |
| Overlays | Modal, Drawer, Menu, MenuBar, ContextMenu, HoverCard, LoadingOverlay, ConfirmModal, Popover, Spotlight, Tooltip |
| Feedback | Alert, Loader, Progress, RingProgress, Notification, ToastStack |
| Data | Badge, Divider, Avatar, AvatarGroup, List, Table, TableView, DataView, TreeView, TabBar, Image, Timeline, Tabs, Accordion |
| Charts | Sparkline, BarChart, LineChart, PieChart |
| Navigation | Breadcrumbs, NavLink, Stepper, Pagination, StatusBar |
| Polish | Icon, ActionIcon, ThemeIcon, CloseButton, CopyButton, Anchor, Code, Kbd, Chip, Indicator, Skeleton, SegmentedControl |
| Motion | Transition, Collapse |
Inputs come in two flavors that match how each control behaves in gpui:
- Controlled (
Checkbox,Switch,Radio,Rating, and theRadioGroup/CheckboxGroupwrappers) areRenderOncebuilders — the parent view owns the value and passes a change handler viacx.listener(...). - Stateful (
TextInput,TextArea,NumberInput,PasswordInput,PinInput,Select,Combobox,Slider,RangeSlider,ColorInput,TagsInput) are gpui entities that own their buffer / selection. Create withcx.new(...)and subscribe to their events.Fieldis the shared label/description/error chrome these compose.
Overlays paint above the page (a deferred layer): Modal and Drawer are
controlled backdrops (render while opened, pass on_close), ConfirmModal
is a confirm/cancel dialog on Modal, Menu is a keyboard-navigable trigger +
deferred action list, MenuBar is a themed in-window application menu
(File / Edit / View …), ContextMenu opens at the pointer on right-click,
Popover is the reusable anchored-floating primitive, HoverCard its
hover-triggered sibling, LoadingOverlay a dimming busy layer over one
container, Spotlight is a command palette, and Tooltip plugs into gpui's
built-in .tooltip(...) via the tooltip(...) helper.
Feedback components communicate state: Alert (inline callout), Loader
(animated pulsing dots/bars), Progress / RingProgress (determinate bar and
circular gauge), Notification (an elevated toast card), and ToastStack (a
positioned, stacking toast manager).
Data display: Avatar, List, Table, Image, and Timeline are stateless
builders; Tabs and Accordion are stateful entities whose panel content is a
builder closure (|window, app| ...) re-invoked each frame so panels can show
live data. The richer views are also entities: TableView<T> (typed rows with
sorting and selection), DataView<T> (a list/grid bound to a
Signal<Vec<T>> with filter/sort projections), TreeView (expandable
hierarchy), and TabBar (a document-style tab strip with close/add buttons).
Editor is a code
editor entity with a line-number gutter and Rust / SQL / JSON highlighting, and
the chart module's Sparkline / BarChart / LineChart / PieChart are
stateless canvas-painted builders.
Navigation: Breadcrumbs, NavLink, and Stepper are stateless builders;
Pagination is a stateful entity (windowed page list with ellipses);
StatusBar is a themed app shell with left/center/right slots. The gallery also
wires a native window menu (cx.set_menus) whose actions toggle the theme
and quit.
A Flutter-flavored layout kit on top of gpui's flexbox: Row, Column,
Container, Padding, Align, Center, Expanded/Flexible (real flex
weights), Spacer, SizedBox, Stack/Positioned, and Wrap, with
MainAxisAlignment / CrossAxisAlignment / EdgeInsets. It is not
glob-exported (names overlap with guise::layout); import it as
use guise::flex::*.
Terse builders, available from the prelude:
col![
row![avatar, name, Spacer::new(), actions], // guise::flex::Row
divider,
body,
]row!/col!/zstack!/wrap! build flex containers; vstack!/hstack!
build the themed layout::Stack/Group. (It's col!, not column!, to avoid
the std column! macro.)
There's also a CSS-like style! block for inline styling, applied with .apply:
gpui::div().apply(style! {
display: flex;
direction: column;
gap: 8;
padding: 16;
radius: 12;
background: "#11151c";
color: color!(rgb(230, 230, 230));
border: color!("#2a2f3a");
})Numbers are pixels; colors take a CSS string or any color!/Hsla. See
Macros.
A small React-flavored layer over gpui's reactivity:
// Create state and provide it (React's useState + Context.Provider).
let count = use_state(cx, 0i32);
provide(cx, count.clone());
// In a descendant view's constructor: read it back and subscribe.
let count = use_context::<Signal<i32>>(cx).unwrap();
watch(cx, &count); // re-render this view when it changes
// Anywhere with app/context access:
count.update(cx, |n| *n += 1); // notifies every watcherSignal<T>— a clonable observable state cell (all clones share one value).provide/use_context— Context/Provider, keyed by type (the gpui idiom).use_state/watch— hook-style helpers.use_form/FormState— field values, validators, and errors keyed by name, with built-inrequired/min_len/emailvalidators.
Binding<T> is a SwiftUI-style two-way connection — a getter + setter over
App. Controlled builders accept one via .bind(...); stateful entities bind
to a Signal with X::bind(entity, &signal, cx) — either way the value flows
both directions with no hand-written change handler:
let dark = use_state(cx, false);
let toggle = Switch::new("dark-mode").bind(dark.binding()); // builder: two-way
let query = use_state(cx, String::new());
let input = cx.new(|cx| TextInput::new(cx).placeholder("Filter…"));
TextInput::bind(&input, &query, cx); // entity: edits land in the signalSignal::binding() wraps the whole signal, signal.lens(get, set) projects
one field of a struct signal, and binding.map(from, into) converts types both
ways. use_memo derives a signal that recomputes when its source changes;
use_effect runs a side effect on change. See
Reactive state.
Mount animations on gpui's animation API: Transition plays a one-shot
fade/slide as its child appears (TransitionKind::Fade | SlideUp | SlideDown | SlideLeft | SlideRight), and Collapse reveals gated content with a fade.
Transition::new("hero").kind(TransitionKind::SlideUp).child(card)
Collapse::new("details").open(self.expanded).child(detail)Colored components share Mantine's variant system: Filled, Light,
Outline, Subtle, Default, Transparent, White.
cargo add guise-uior in Cargo.toml:
[dependencies]
guise-ui = "0.2"
gpui = "0.2"The crate is published on crates.io as
guise-ui(theguisename was taken), but its library is namedguise— so you still writeuse guise::prelude::*;.
guise tracks the published gpui 0.2
release on crates.io.
Requires Rust stable.
cargo run -p gallery # launch the component gallery
cargo test -p guise-ui # run the library's unit testsMIT — see LICENSE.