Rust GUI frameworks
2018 Framework 2026: Sunset
07

Druid, the retired bet

The “official” Rust GUI effort taught the field what to reuse.

Druid (November 2018) was the Linebender team's first toolkit and, for a few years, the most visible "official" Rust GUI bet. Its design has three ideas: your app state is a value that implements Data (cheap to compare), widgets are generic over that state, and the tree is built once. This is the classic counter.

druid-counter/src/bin/one.rs ✔ compiled · druid 0.8 · rustc 1.95.0
use book_error::{Error, Result};
use druid::widget::{Button, Flex, Label};
use druid::{AppLauncher, Widget, WidgetExt, WindowDesc};

fn ui() -> impl Widget<u32> {
    let label = Label::dynamic(|n: &u32, _| format!("count: {n}")).padding(5.0).center();
    let button = Button::new("increment").on_click(|_ctx, n, _env| *n += 1).padding(5.0);
    Flex::column().with_child(label).with_child(button)
}

fn main() -> Result<()> {
    AppLauncher::with_window(WindowDesc::new(ui()).title("druid"))
        .launch(0)
        .map_err(Error::other)?;
    Ok(())
}

The third idea is the Lens. To reuse a widget on one field of a larger state, you wrap it in a lens that focuses on that field. The derive macro generates the lenses for you.

druid-counter/src/bin/two.rs ✔ compiled · druid 0.8 · rustc 1.95.0
use book_error::{Error, Result};
use druid::widget::{Button, Flex, Label};
use druid::{AppLauncher, Data, Lens, Widget, WidgetExt, WindowDesc};

// Druid's model: your state is `Data`, and `Lens`es focus widgets onto fields.
// Xilem's map_state is the descendant of this idea.
#[derive(Clone, Data, Lens)]
struct State {
    likes: u32,
    dislikes: u32,
}

fn counter(name: &'static str) -> impl Widget<u32> {
    Flex::row()
        .with_child(Label::dynamic(move |n: &u32, _| format!("{name}: {n}")))
        .with_child(Button::new("+").on_click(|_, n, _| *n += 1))
}

fn ui() -> impl Widget<State> {
    Flex::column()
        .with_child(counter("likes").lens(State::likes))
        .with_child(counter("dislikes").lens(State::dislikes))
}

fn main() -> Result<()> {
    AppLauncher::with_window(WindowDesc::new(ui()))
        .launch(State { likes: 0, dislikes: 0 })
        .map_err(Error::other)?;
    Ok(())
}

Druid was retired: the team's work moved to Xilem, whose lens function (see that page) is the direct descendant of this design. Pieces of Druid survive. kurbo (2D geometry) still underpins Vello, and the piet drawing abstraction, which Druid used with piet-common and cairo on Linux, has been superseded by Vello. The crate is at 0.8.3 with 29 dependents. Notice what is missing from its lockfile: winit. Druid had its own platform layer, druid-shell.

crates.io first publish.