vizia: a Rust GUI framework built around a view tree and signals
A declarative GUI library written in Rust
At a glance
- What is it?
- A declarative desktop GUI library for Rust that draws through skia, styles with a CSS-like sheet, and recomputes only what application state actually changed.
- Who is it for?
- vizia is at its best when you want a native desktop binary from Rust and do not want to hand-maintain an immediate mode rendering loop. The signal rewrite in 0.4.0 is the reason to look now rather than a year ago, and the layout and styling layers are far more finished than the widget count suggests.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 51 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A view tree you declare in Rust rather than a render loop you maintain
The counter application in the README is the whole pitch in forty lines. You define a struct holding a Signal, implement a Model trait with one event handler, and then describe the widget tree inside the Application constructor. Nothing in that file draws anything, clears a frame, or invalidates a region. The views are derived from state, and the framework works out what needs repainting when that state changes.
The claim that this is declarative is stronger than it first sounds, because vizia does not use a markup file or a macro DSL. `use vizia::prelude::*` brings the widget types into scope as ordinary Rust items, and the modifiers on a view are method calls: `.on_press`, `.width(Pixels(50.0))`, `.alignment(Alignment::Center)`. If you have written Rust before, the structure will feel closer to constructing a data structure than to writing a user interface, which is exactly the pitch.
```rust, no_run use vizia::prelude::*;
// Define some model data pub struct AppData { count: Signal<i32>, }
// Define events to mutate the data pub enum AppEvent { Increment, } ```
The label bound to the counter is the part worth pausing on. `Label::new(cx, count)` is handed the Signal itself rather than a rendered string, and `.width(Pixels(50.0))` reserves space so the layout does not jitter as digits grow. Binding to a value rather than to a formatted snapshot is the design decision that makes the rest of the framework coherent.
Signals replaced lenses in the 0.4.0 release, and the migration doc stayed in the tree
The 0.4.0 release notes list the change that matters most for anyone arriving late: lenses have been replaced by a reactivity system built on signals. Everything else in that release builds on it, including CSS variable support, right to left layout and fluent datetime helpers in localization, and a round of performance work concentrated on layout.
The repository keeps a file called MIGRATION_LENS_TO_SIGNALS.md at its root, next to ARCHITECTURE.md. That is unusual and useful. A reactivity rewrite in a library that has already shipped three tagged releases breaks code, and leaving the conversion guide in the tree rather than only in release notes means a reader can check what a given lens became. If you have seen older vizia examples in a blog post, that file is the first place to look, because those examples will not read the same way.
Version numbering tells its own small story. The workspace package version is 0.4.0, matching the tagged release from April 2026, and the last push recorded for the repository is 2026-08-21. So the working tree has moved months past the published crate, which is the ordinary state for a library maintained on a `main` branch. Nothing is archived, and 66 issues are open.
The feature list in Cargo.toml decides which backends you compile in
The root Cargo.toml is more informative about real usage than the README feature list, because it enumerates exactly what you can leave out. The default set is `winit`, `clipboard`, `x11`, `wayland`, `markdown` and `accesskit`, which means a default build already pulls in both X11 and Wayland backends, a markdown renderer, and the accessibility bridge that lets screen readers see your widget tree.
[features]
default = ["winit", "clipboard", "x11", "wayland", "markdown", "accesskit"]
baseview = ["vizia_baseview"]
rayon = ["vizia_core/rayon"]
serde = ["vizia_core/serde"]
tokio = ["vizia_core/tokio", "dep:tokio"]
url-loader = ["vizia_core/url-loader"]The optional entries cover a spread of different ambitions. `baseview` swaps the windowing backend for audio plugin work, which the README links to the nih-plug framework. `tokio` matters if you want async tasks feeding the UI, and the examples directory contains a `task_download.rs` that presumably exercises exactly that. `rayon` is the parallel offload option. `url-loader` suggests remote asset fetching.
The workspace is declared with edition 2024 and a minimum Rust of 1.85, so this is not a library you can build with an old toolchain. The crate excludes `assets/` and `examples/` from the published package, which is why the gallery is only runnable from a clone.
Layout, stylesheets, and a gallery worth running before anything else
Layout is delegated to morphorm, a separate flexbox implementation maintained by the same author. That is the right call for a library that has to place labels of unknown width next to each other without you computing sizes yourself, and it is why the counter example does not mention measurement at all.
Styling is described as stylesheets with hot reloading, and the examples tree backs that up with both `stylesheet.rs` and `variables.rs`, the latter matching the CSS variable support added in 0.4.0. The built-in set is reported as more than 25 views and two themes, light and dark, with more than 4,250 SVG icons from the Tabler set. Those counts come from the README rather than from anything verifiable in the tree, so treat them as a claim rather than a count you can check.
The most useful thing in the repository for a first-time visitor is the widget gallery, a workspace member rather than a loose example:
cargo run --release -p widget_galleryRunning it before writing anything tells you how the default theme treats spacing, focus rings, and disabled state, which is exactly the knowledge you would otherwise acquire by trial and error one widget at a time.
The example command in the README does not match the crate's example settings
Here is a contradiction worth knowing about before you type it. The README says a simple counter application can be run with `cargo run --example counter`, but the root Cargo.toml sets `autoexamples = false`, which tells Cargo not to auto-discover example targets at all. The examples recorded in the repository cover animation, drag and drop, keymap, l10n, multiwindow, number input, popup windows, rich text, save dialogs, stylesheets, task download, timers, variables and window modifiers, along with directories for 7GUIs, gallery, todo, views and widget gallery. There is no counter example file among them.
The counter code does appear in the README, so it is real working code, just not a target that the build will find by that name. Treat the README's generic form as the reliable instruction and copy the file into a target that exists:
cargo run --release --example name_of_exampleThe README also draws a distinction worth keeping, between loose examples and packages that have their own Cargo.toml. The gallery is the second kind, which is why it needs `-p widget_gallery` rather than `--example`. A second, smaller oddity sits in the same README: it points at the `examples` directory on the default branch while the Cargo workspace members list lives in the root manifest, so a clone is the only way to get either view of the same thing.
What the repository answers and where the documentation takes over
Set against what it does not say, the README is a fair summary of a framework that is genuinely further along than a first impression suggests. Rendering goes through skia with an added layer that only draws what is necessary, localization uses the fluent project, accessibility runs through accesskit, and animation is a named feature rather than something bolted on afterwards. Cross platform means Windows, Linux and macOS through the same widget code.
What the repository cannot answer is anything about behaviour under load, about how a deeply nested tree performs, or about how the two styling systems, a builder-style modifier chain and a stylesheet, interact when they both touch the same property. Nor does it say much about text shaping, fonts, or right to left rendering beyond the release note that layout support improved.
The project points at two separate documentation sites. book.vizia.dev is a prose guide meant to be read front to back, and docs.vizia.dev is auto-generated API documentation generated from the code. For a library whose central idea is that you describe a tree and the framework resolves it, those are the places where the resolution rules are written down. The README is a good advertisement for the model and a poor substitute for the specification.
Editorial conclusion
vizia is at its best when you want a native desktop binary from Rust and do not want to hand-maintain an immediate mode rendering loop. The signal rewrite in 0.4.0 is the reason to look now rather than a year ago, and the layout and styling layers are far more finished than the widget count suggests. What the repository cannot tell you is how the framework behaves under your own layout constraints, because the interesting material lives in the book and the generated docs rather than the README. Start with the widget gallery so you can see every built-in view before writing any, then read the book chapters on layout and on signals, and only afterwards commit to the framework.
Frequently asked questions
What is vizia used for?
vizia is a Rust library for building native desktop applications with a graphical user interface. You describe a tree of views and bind widgets to signals holding your data, and the framework recomputes and repaints only what changed. It targets Windows, Linux and macOS through the winit backend, with an optional baseview backend for audio plugin hosts.
Is vizia mature enough for production?
It has shipped three tagged releases, the latest being 0.4.0 in April 2026, and the workspace carries edition 2024 with a minimum Rust of 1.85. The repository is not archived and the last push was 2026-08-21. It is pre-1.0, so treat the API as something that can still move, and read the migration file at the repository root before following any older example you find online.
How do I run the vizia widget gallery?
The gallery is a workspace package rather than a loose example, so it needs the package flag instead of the example flag: run it with cargo run --release -p widget_gallery from a clone of the repository. Loose examples use cargo run --release --example followed by the example name.
What replaced lenses in vizia 0.4.0?
The 0.4.0 release replaced the lens based reactivity system with one built on signals. The repository keeps a conversion guide named MIGRATION_LENS_TO_SIGNALS.md at its root, alongside an ARCHITECTURE.md file. Older examples written against lenses will not read the same way, so that file is the place to check what a given lens became.