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,
120+ composable components, a reactive state layer with two-way bindings, and
the full Lucide icon set embedded as the default icons —
no asset pipeline needed.
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/ (also rendered at
wess.github.io/guise):
- Tutorial — build a complete app step by step (web version)
- App walkthrough — a project tracker wired the way a real guise app fits together
- Getting started · Theming · Component model
- Components: Buttons · Icons · Inputs · Dates & times · File handling · Typography · Layout · Panels · Feedback · Data · Charts · Editor · Markdown editor · Overlays · Navigation
- Systems: Flex layout · Macros · Transitions & animation · Drag & drop · 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.
Two other component libraries target gpui: gpui-component (shadcn-flavored, backs Longbridge Pro) and adabraka-ui. A quick orientation, as of July 2026:
| guise | gpui-component | adabraka-ui | |
|---|---|---|---|
| Design language | Mantine | shadcn/ui | shadcn/ui |
| Components | 120+ | 60+ | ~140 |
| Reactive layer | Signal / Binding / lenses, reactive Form |
— (entities + subscriptions) | — |
| Icons | all 1,991 Lucide glyphs as an embedded font, zero setup | 99 Lucide SVGs via an assets crate | ~1,600 SVGs, copied into your app manually |
| Theming | Mantine palette, JSON theme files, 6 presets, per-slot overrides | ~140 tokens, JSON themes with hot reload, 22 presets | 19 presets, theme behind a global Mutex |
| Code editor | 10-language highlighter + diagnostics API | tree-sitter (~35 languages) + LSP client | tree-sitter (22 languages) |
| Docking / panels | PaneGroup splits-with-tabs + layout persistence |
DockArea + floating Tiles + serialization |
resizable/split panels |
| Charts | 6 types with axes, legends, hover | 6 types incl. candlestick & Sankey on a plot framework | 11 types |
| Motion | easing curves, spring physics, exit animations | basic easing | large effects library |
| Drag & drop | typed payloads, sortable lists | panel docking | draggable + sortable |
| Date/time pickers | yes | yes (incl. range presets) | yes |
| Tests | 300+ incl. gpui entity harness | ~580 incl. render tests | minimal |
| gpui dependency | crates.io releases | crates.io releases; dev tracks zed main | a custom gpui fork |
| License | MIT | Apache-2.0 | MIT |
Reach for guise if you want Mantine's ergonomics, SwiftUI-style two-way bindings, and icons that just work with zero asset setup. gpui-component is the bigger ecosystem — a production code editor with LSP, a full dock system, and a WASM story. adabraka-ui ships the largest effects/animation collection.
Install a theme once at startup; every component reads it from the gpui global:
guise::Theme::dark().init(cx); // or Theme::light()
guise::Theme::catppuccin().init(cx); // or nord / tokyonight / gruvbox / dracula / solarized_lightThe 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, plus success /
warning / danger / info feedback accents). Themes also load from flat
JSON files — Theme::from_json(source) — with no serde dependency.
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, Breakpoint/Responsive |
| Surface | Paper, Card |
| Typography | Text, Title, Mark, Blockquote, Spoiler |
| Inputs | Button, TextInput, TextArea, NumberInput, PasswordInput, PinInput, Checkbox, Switch, Radio, RadioGroup, CheckboxGroup, Select, Combobox, Autocomplete, Slider, RangeSlider, Rating, ColorInput, TagsInput, Transfer, Field |
| Dates & files | Calendar, DatePicker, TimePicker, FileInput, Dropzone (with pure Date/Time models) |
| Editor | Editor (highlighting for Rust, SQL, JSON, TOML, Python, JS/TS, Go, C, Markdown; LSP-shaped diagnostics), MarkdownEditor (Obsidian-style live preview) |
| Overlays | Modal, Drawer, Menu, MenuBar, ContextMenu, HoverCard, LoadingOverlay, ConfirmModal, Popover, Spotlight, Tooltip, Tour, OverlayHost (window-level modal stack + toasts) |
| Feedback | Alert, Loader, Progress, RingProgress, Notification, ToastStack |
| Data | Badge, Divider, Avatar, AvatarGroup, List, VirtualList, Table, TableView, DataView, TreeView, TabBar, Image, Timeline, Tabs, Accordion, Carousel |
| Charts | Sparkline, LineChart, AreaChart, BarChart, ScatterChart, PieChart — with optional axes, legends, and hover readouts |
| Navigation | Breadcrumbs, NavLink, NavigationMenu, Stepper, Pagination, StatusBar |
| Drag & drop | Draggable, DropTarget, SortableList — typed payloads |
| Motion | Transition, Collapse (true height animation), Presence (exit animations), Easing curves + Spring physics |
| Polish | Icon (all of Lucide embedded), ActionIcon, ThemeIcon, CloseButton, CopyButton, Anchor, Code, Kbd, Chip, Indicator, Skeleton, SegmentedControl |
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,Autocomplete,Slider,RangeSlider,ColorInput,TagsInput,DatePicker,TimePicker,FileInput,Transfer) 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, ConfirmModal a confirm/cancel dialog, Menu a
keyboard-navigable action list, MenuBar a themed in-window application menu,
ContextMenu opens at the pointer, Popover is the anchored-floating
primitive, HoverCard its hover-triggered sibling, Spotlight a command
palette, Tour a step-by-step onboarding walkthrough, and OverlayHost
owns a window-level modal stack and toast queue — open dialogs from any
handler, focus restores on close.
Data display scales from stateless builders (Avatar, List, Table,
Timeline) to virtualized, signal-bound entities: TableView<T> (typed rows,
sorting, selection, virtualized body), DataView<T> (list/grid over a
Signal<Vec<T>> with filter/sort projections and windowed rendering),
TreeView (expandable hierarchy, virtualizable), and VirtualList (100k rows
render as cheaply as 20). PaneGroup is the Zed-style splits-with-tabs
workspace, including drag-to-split and layout snapshots that persist across
sessions.
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. There's also a CSS-like style!
block for inline styling — see
Macros.
A small React-flavored layer over gpui's reactivity:
let count = use_state(cx, 0i32); // Signal<i32>
provide(cx, count.clone()); // Context.Provider, keyed by type
watch(cx, &count); // re-render this view on change
count.update(cx, |n| *n += 1); // notifies every watcherBindings are SwiftUI-style two-way connections: controlled builders take
.bind(signal.binding()), stateful entities bind with
X::bind(&entity, &signal, cx) — the value flows both directions with no
hand-written change handlers. signal.lens(get, set) projects one struct
field; binding.map(from, into) converts types both ways.
Forms: every Form field is its own Signal<String>, so inputs bind
straight to fields; rules (including cross-field like equals_field) run on
submit and errored fields re-validate live as they're edited:
let form = Form::new(cx)
.field(cx, "email", "")
.rule("email", validators::email());
TextInput::bind(&email_input, &form.signal("email"), cx);
if let Some(values) = form.submit(cx) { /* … */ }See Reactive state.
Easing curves (including a CSS cubic-bezier solver), closed-form Spring
physics, Transition entrances, a Collapse that animates real height both
directions, and Presence for exit animations on conditionals:
Collapse::new("details")
.open(self.expanded)
.height(120.0)
.easing(Easing::Spring(Spring::default()))
.child(detail_panel())Colored components share Mantine's variant system: Filled, Light,
Outline, Subtle, Default, Transparent, White.
guise builds against crates.io gpui 0.2.2 — no git pins, no patch
sections:
[dependencies]
guise-ui = "0.10"
gpui = "0.2.2"The crate is named
guise-ui(theguisename was taken on crates.io), but its library is namedguise— so you still writeuse guise::prelude::*;.
Pinning via git works too:
guise-ui = { git = "https://github.com/wess/guise", tag = "v0.10.0" }Requires Rust stable.
cargo run -p gallery # launch the component gallery
cargo test -p guise-ui # run the library's tests (unit + gpui entity harness)MIT — see LICENSE.