Snow UI is an elegant, declarative UI framework for Rust. One codebase, every platform—from desktop to mobile to web.
"The UI is a projection of a virtual world onto the screen."
Snow UI models your application as a virtual world. Elements are objects in that world—each with properties, behaviors, and the ability to communicate via messages. Rendering is simply projecting that world onto the display.
Snow UI targets all major platforms from a single Rust codebase—no runtime, no VM, just native performance everywhere.
Snow UI combines Rust's safety and performance with a modern, declarative API for building rich user interfaces.
No JavaScript, no C++ glue, no foreign runtimes. Your UI is 100% Rust, compiled to native code (or WASM for the web).
Describe your UI with expressive macros like obj! and list!.
Clear, readable code that maps directly to the visual structure.
Built on Tokio, event handlers and actions are fully async. Network calls, timers, and animations are first-class citizens.
Define reusable elements with the #[element] macro.
Compose complex UIs from simple, isolated building blocks.
Decouple components with an event bus. Send typed messages between elements without tight coupling or shared mutable state.
Wrap values in State<T> and the UI automatically
re-renders whenever state changes—no boilerplate required.
ServerApi makes it easy to call REST endpoints.
Pair it with Form for complete data-submission workflows.
Board, Card, Row, Text, Button, TextInput, Form, Switch, TextClock and more—a growing library of ready-to-use UI primitives.
IntervalTimer fires messages on a schedule.
Build clocks, animations, and polling loops with minimal code.
A few real examples from the Snow UI repository.
use snow_ui::prelude::*; fn world() -> World { World { root: obj!(Board { width: VIEWPORT_WIDTH, height: VIEWPORT_HEIGHT, h_align: HAlign::Center, v_align: VAlign::Middle, children: list![Card { children: list![ Row { children: list![Text { text: "Clock Example ⏰", }], }, Row { children: list![TextClock { format: "%H:%M:%S", }], }, ], }], }), ..default() } } fn main() { snow_ui::launch(world); }
use snow_ui::prelude::*; use tokio::time::Duration; #[message] struct SimpleTextTimerTickEvent {} #[element] struct SimpleTextTimer { seconds: State<u128>, timer: IntervalTimer<SimpleTextTimerTickEvent>, } register_handler!( impl MessageHandler<SimpleTextTimerTickEvent> for SimpleTextTimer { async fn handle(&mut self, _: &SimpleTextTimerTickEvent, _: &mut MessageContext) { self.seconds.update(|s| *s += 1); } } ); fn simple_text_timer() -> Object { obj!(SimpleTextTimer { seconds: State::new(0), timer: IntervalTimer::from_interval( Duration::from_secs(1) ), }) } fn main() { snow_ui::launch(world); }
use snow_ui::prelude::*; #[message] struct IncreaseButtonClicked {} #[element] struct IncreaseButton { button: Button } impl ClickHandler for IncreaseButton { async fn on_click(&mut self) { event_bus().send(IncreaseButtonClicked {}); } } #[element] struct SimpleText { count: State<u128> } register_handler!( impl MessageHandler<IncreaseButtonClicked> for SimpleText { async fn handle(&mut self, _: &IncreaseButtonClicked, _: &mut MessageContext) { self.count.update(|c| *c += 1); } } ); fn world() -> World { World { root: obj!(Board { children: list![Card { children: list![ Row { children: list![increase_button()], }, Row { children: list![simple_text()], }, ], }], }), ..default() } } fn main() { snow_ui::launch(world); }
use snow_ui::prelude::*; #[message] struct LoginSuccess {} async fn login(form: &Form) -> anyhow::Result<()> { let json = form.to_json()?; let api = ServerApi::new("https://api.example.com/login"); api.post_json(&json).await?; event_bus().send(LoginSuccess {}); Ok(()) } fn login_board() -> Object { obj!(LoginBoard { board: Board { children: list![Form { submit_handler: login, submit_button: Button { text: "Login" }, reset_button: Button { text: "Reset" }, children: list![ Row { children: list![TextInput { label: "Username", name: "username", max_len: 20, }], }, Row { children: list![TextInput { label: "Password", name: "password", r#type: "password", max_len: 20, }], }, ], }], } }) } fn main() { snow_ui::launch(world); }
Star the project on GitHub and follow along as it grows.