| Crates.io | allui |
| lib.rs | allui |
| version | 0.1.0 |
| created_at | 2026-01-11 01:38:32.820796+00 |
| updated_at | 2026-01-11 01:38:32.820796+00 |
| description | A SwiftUI-inspired declarative UI framework for Rust, built on gpui-rs |
| homepage | https://github.com/AntimaterialLabs/allui-rs |
| repository | https://github.com/AntimaterialLabs/allui-rs |
| max_upload_size | |
| id | 2035082 |
| size | 443,071 |
A SwiftUI-inspired UI framework for Rust, built on GPUI and gpui-component.
Allui brings SwiftUI's declarative, composable API to Rust desktop applications. If you know SwiftUI, you already know Allui. If you know Jetpack Compose, it’s close enough.
The Problem: Building desktop UIs in Rust typically means either low-level graphics programming or fighting with paradigms that don't fit the language. SwiftUI proved that declarative UI can be both powerful and ergonomic, but it's locked to Apple platforms.
The Solution: Allui provides SwiftUI's mental model and API patterns on top of GPUI (Zed editor's GPU-accelerated UI framework). You get:
.padding().background() differs from .background().padding(), just like SwiftUI┌─────────────────────────────────────────────────────────────┐
│ Your Application │
├─────────────────────────────────────────────────────────────┤
│ Allui │
│ ┌─────────────────────┐ ┌────────────────────────────┐ │
│ │ Layout Primitives │ │ Components │ │
│ │ ───────────────── │ │ ──────────────────── │ │
│ │ VStack, HStack │ │ Text, Button, Toggle │ │
│ │ ZStack, Spacer │ │ TextField, Slider │ │
│ │ ScrollView, List │ │ Picker, ProgressView │ │
│ │ LazyVStack/HStack │ │ Image, Label, Link │ │
│ │ Grid, LazyVGrid │ │ │ │
│ │ LazyHGrid │ │ │ │
│ └─────────────────────┘ └────────────────────────────┘ │
│ ┌─────────────────────┐ ┌────────────────────────────┐ │
│ │ Modifier System │ │ Control Flow │ │
│ │ ───────────────── │ │ ──────────────────── │ │
│ │ padding, frame │ │ ForEach, If, IfLet │ │
│ │ background, border │ │ Section, GridRow │ │
│ │ corner_radius │ │ GridItem │ │
│ │ shadow, opacity │ │ │ │
│ └─────────────────────┘ └────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ gpui-component │
│ (Stateful widgets: Switch, Input, etc.) │
├─────────────────────────────────────────────────────────────┤
│ GPUI │
│ (GPU-accelerated rendering engine) │
└─────────────────────────────────────────────────────────────┘
Add Allui to your Cargo.toml:
[dependencies]
allui = "0.1"
gpui = "0.2"
gpui-component = "0.5"
Create a simple view:
use gpui::{prelude::*, App, Context, Window};
use allui::prelude::*;
struct CounterView {
count: i32,
}
impl Render for CounterView {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let count = self.count;
VStack::new()
.spacing(16.0)
.child(
Text::new(format!("Count: {}", count))
.font(Font::title())
)
.child(
HStack::new()
.spacing(12.0)
.child(
Button::new("-", cx.listener(|this, _, _, _| {
this.count -= 1;
}))
.button_style(ButtonStyle::Bordered)
)
.child(
Button::new("+", cx.listener(|this, _, _, _| {
this.count += 1;
}))
.button_style(ButtonStyle::BorderedProminent)
)
)
.padding(24.0)
.background(Color::secondary_system_background())
.corner_radius(12.0)
}
}
Allui uses the same stack-based layout system as SwiftUI:
// Vertical stack - centers horizontally by default
VStack::new()
.spacing(8.0)
.alignment(HorizontalAlignment::Leading)
.child(Text::new("Title"))
.child(Text::new("Subtitle"))
// Horizontal stack - centers vertically by default
HStack::new()
.spacing(12.0)
.child(Image::system_name("star"))
.child(Text::new("Favorites"))
.child(Spacer::new())
.child(Text::new("12"))
// Overlay stack - centers in both axes
ZStack::new()
.child(background_image)
.child(overlay_text)
Modifiers wrap views in container elements. Order matters:
// Padding INSIDE the background (blue box with internal padding)
Text::new("Hello")
.padding(16.0)
.background(Color::blue())
// Padding OUTSIDE the background (blue box with external spacing)
Text::new("Hello")
.background(Color::blue())
.padding(16.0)
Available modifiers:
| Category | Modifiers |
|---|---|
| Layout | padding, frame, frame_size, frame_width, frame_height, fixed_size, aspect_ratio |
| Visual | background, foreground_color, corner_radius, border, shadow, opacity |
| Behavior | hidden, disabled, on_tap_gesture |
// Basic scroll view
ScrollView::new("my-scroll")
.axes(ScrollAxes::vertical())
.child(
VStack::new()
.children(items.iter().map(|item| ItemRow::new(item)))
)
// iOS-style grouped list
List::new("settings")
.list_style(ListStyle::inset_grouped())
.child(
Section::new()
.header("Account")
.child(Text::new("Profile"))
.child(Text::new("Privacy"))
)
.child(
Section::new()
.header("General")
.child(Text::new("Notifications"))
)
// Iterate over collections
VStack::new()
.children(ForEach::new(&items, |item| {
Text::new(item.name.clone())
}))
// Conditional rendering
If::new(is_logged_in)
.then(|| ProfileView::new())
.otherwise(|| LoginView::new())
// Optional content
IfLet::new(selected_user, |user| {
Text::new(format!("Hello, {}!", user.name))
})
For large datasets, use lazy stacks that only render visible items:
impl Render for MyListView {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
LazyVStack::new(cx.entity().clone(), "item-list", &self.scroll_handle)
.item_count(self.items.len())
.item_height(44.0)
.render_item(|view, index, _, _| {
Text::new(&view.items[index])
.padding(12.0)
})
.build(_window, cx)
}
}
Allui provides both static and lazy grid layouts:
// Static Grid - auto-sizing columns based on content
Grid::new()
.horizontal_spacing(16.0)
.vertical_spacing(8.0)
.child(
GridRow::new()
.child(Text::new("Name"))
.child(Text::new("Value"))
)
.child(
GridRow::new()
.child(Text::new("Width"))
.child(Text::new("100px"))
)
// LazyVGrid - vertically-scrolling grid with fixed columns
let columns = vec![
GridItem::flexible(),
GridItem::flexible(),
GridItem::fixed(100.0),
];
LazyVGrid::new(cx.entity().clone(), “photo-grid", &self.scroll_handle)
.columns(columns)
.spacing(8.0)
.item_count(photos.len())
.render_item(|view, index, _, _| {
PhotoCell::new(&view.photos[index])
})
.build(window, cx)
// LazyHGrid - horizontally-scrolling grid with fixed rows
let rows = vec![GridItem::fixed(80.0), GridItem::fixed(80.0)];
LazyHGrid::new(cx.entity().clone(), “category-grid", &self.scroll_handle)
.rows(rows)
.spacing(12.0)
.item_count(items.len())
.render_item(|view, index, _, _| {
CategoryCard::new(&view.items[index])
})
.build(window, cx)
| Component | Description |
|---|---|
Text |
Styled text with font and color options |
Label |
Icon + text combination |
Image |
Display images from files, URLs, or system icons |
Divider |
Visual separator line |
ProgressView |
Spinner or progress bar |
Link |
Tappable text that triggers actions |
| Component | Description |
|---|---|
Button |
Tappable button with multiple styles |
Toggle |
Boolean switch |
TextField |
Single-line text input |
SecureField |
Password input (masked) |
TextEditor |
Multi-line text input |
Slider |
Range value selection |
Stepper |
Increment/decrement control |
Picker |
Selection from options |
| Component | Description |
|---|---|
VStack |
Vertical stack layout |
HStack |
Horizontal stack layout |
ZStack |
Overlay/layered layout |
Spacer |
Flexible space |
ScrollView |
Scrollable container (vertical, horizontal, or both) |
List |
iOS-style sectioned list |
Grid |
Static 2D table layout with auto-sizing columns |
LazyVStack |
Virtualized vertical list |
LazyHStack |
Virtualized horizontal list |
LazyVGrid |
Virtualized vertical grid with fixed columns |
LazyHGrid |
Virtualized horizontal grid with fixed rows |
Run the interactive storybook to see all components:
cargo run --example storybook
Before using any gpui-component widgets (Toggle, TextField, etc.), initialize:
fn main() {
Application::new().run(|cx: &mut App| {
gpui_component::init(cx); // Required!
// ... rest of your app
});
}
Allui components are presentational. State lives in your GPUI view:
struct MyView {
toggle_value: bool,
}
impl Render for MyView {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
Toggle::new_with_handler(
"Enable Feature",
self.toggle_value,
cx.listener(|this, checked: &bool, _, cx| {
this.toggle_value = *checked;
cx.notify(); // Trigger re-render
})
)
}
}
MIT License - see LICENSE.md for details.