5 unstable releases
Uses new Rust 2024
| 0.7.1 | May 9, 2026 |
|---|---|
| 0.7.0 | May 9, 2026 |
| 0.4.1 | Mar 22, 2026 |
| 0.4.0 | Mar 22, 2026 |
| 0.0.1 | Mar 21, 2026 |
#292 in GUI
2MB
44K
SLoC
plushie
Build native desktop apps in Rust. Pre-1.0
Write your entire application in Rust (state, events, UI) and get native windows on Linux, macOS, and Windows. The renderer is built on iced and can run in-process (no subprocess needed) or as a separate binary over stdin/stdout.
SDKs are also available for Elixir, Gleam, Python, Ruby, and TypeScript.
Quick start
use plushie::prelude::*;
struct Counter { count: i32 }
impl App for Counter {
type Model = Self;
fn init() -> (Self, Command) {
(Counter { count: 0 }, Command::none())
}
fn update(model: &mut Self, event: Event) -> Command {
match event.widget_match() {
Some(Click("inc")) => model.count += 1,
Some(Click("dec")) => model.count -= 1,
_ => {}
}
Command::none()
}
fn view(model: &Self, _widgets: &mut WidgetRegistrar) -> ViewList {
window("main").title("Counter").child(
column().spacing(8.0).padding(16)
.child(text(&format!("Count: {}", model.count)).size(24.0))
.child(row().spacing(8.0).children([
button("inc", "+").style(Style::primary()),
button("dec", "-").style(Style::danger()),
]))
).into()
}
}
fn main() -> plushie::Result {
plushie::run::<Counter>()
}
[dependencies]
plushie = "0.6"
Direct mode (default) embeds the renderer. For wire-only (lighter build):
[dependencies]
plushie = { version = "0.6", default-features = false, features = ["wire"] }
default-features = false without either runner feature (direct or
wire) is a library-only build. It supports APIs such as queries and
TestSession, but plushie::run() returns Error::NoRunnerFeature
because no renderer runner is compiled in.
Run the examples with cargo run -p plushie --example counter.
How it works
Your Rust application and the renderer share a process by default (direct mode). The SDK builds UI trees and handles events; the renderer draws native windows and captures input.
The SDK diffs each new tree against the previous one and sends only the changes. In wire mode, the renderer runs as a separate process and communication happens over stdin/stdout, using the same protocol that powers the Elixir, Gleam, Python, Ruby, and TypeScript SDKs.
Features
- Elm architecture - init, update, view with typed events and commands
- Built-in widgets - layout, input, display, and interactive widgets out of the box
- Canvas - shapes, paths, gradients, transforms, and interactive elements for custom 2D drawing
- Themes - dark, light, nord, catppuccin, tokyo night, and more, with custom palettes and per-widget style overrides
- Animation - renderer-side transitions, springs, and sequences with no wire traffic per frame
- Multi-window - declare windows in your view; the framework manages the rest
- Platform effects - native file dialogs, clipboard, OS notifications
- Accessibility - keyboard navigation, screen readers, and focus management via AccessKit
- Custom widgets - compose existing widgets, draw on the
canvas, or implement
PlushieWidgetin Rust - Two rendering modes - direct (in-process, default) or wire (subprocess binary via stdin/stdout)
Testing
Use TestSession for headless testing without a display:
use plushie::test::TestSession;
let mut session = TestSession::<Counter>::start();
session.click("inc");
session.click("inc");
assert_eq!(session.model().count, 2);
session.assert_text("count", "2");
Status
Pre-1.0. The core works (built-in widgets, event system, themes, multi-window, testing, accessibility) but the API is still evolving. Pin to an exact version and read the CHANGELOG when upgrading.
License
MIT OR Apache-2.0
Dependencies
~4–57MB
~867K SLoC