# Iced: an Elm-style GUI library for Rust that stays experimental

> Iced splits a Rust GUI into state, messages, view and update, and ships two renderers behind one API. The README still calls it experimental software, so the question is whether its architecture fits your app before you commit.

**iced-rs/iced** — A cross-platform GUI library for Rust, inspired by Elm

- Repository: https://github.com/iced-rs/iced
- Website: https://iced.rs
- Stars: 31,543 · Forks: 1,675
- Language: Rust
- License: MIT
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/iced-rs-iced

## The problem Iced solves for Rust developers

Rust has no standard GUI toolkit. Building a window, handling input and drawing widgets means choosing among bindings to C libraries, immediate-mode toolkits, or webview wrappers. Iced takes a different route: a pure Rust library where the interface is a value produced by a function, and every user interaction is a message the compiler can check.

The target reader is a Rust developer who wants the type safety of the language to extend into the interface layer. The README describes a library "focused on simplicity and type-safety", inspired by Elm. That means no callback soup and no shared mutable widget tree. If your application logic already lives in Rust, Iced keeps it there rather than pushing you into JavaScript or a C++ toolkit.

The project is not a finished product. The README states plainly: "Iced is currently experimental software." That sentence should shape how you read everything else on this page.

## State, messages, view and update: the Elm loop in Rust

The architecture is The Elm Architecture, and the README reduces it to four concepts: State, Messages, View logic, Update logic. State is a plain struct. Messages are an enum of events you care about. View is a function from state to widgets. Update is a function from message to new state.

The README's counter example shows the shape. A Counter struct holds an i32 value. A Message enum has Increment and Decrement variants. The view method builds a column of two buttons and a text widget, and each button carries on_press(Message::Increment) or on_press(Message::Decrement). The update method matches on the message and mutates the value.

The runtime closes the loop. According to the README, Iced takes the result of the view logic and lays out its widgets, processes system events and produces messages for the update logic, then draws the resulting interface. You never call a draw function or wire an event listener by hand.

Underneath, the ecosystem is modular. The repository has separate top-level directories for runtime, renderer, wgpu, tiny_skia, winit, widget and core. The README describes a renderer-agnostic native runtime, two built-in renderers (iced_wgpu for Vulkan, Metal and DX12, and iced_tiny_skia as a software fallback), and a windowing shell. The Cargo.toml default feature set enables wgpu, tiny-skia, crisp, web-colors, thread-pool, linux-theme-detection, x11 and wayland, so a plain install pulls in both renderers.

## Installing Iced and running a first counter

The README does not give a cargo add line, but the crate is published as iced on crates.io, which the badges confirm. The examples directory contains a counter example, and the README shows the full code for one. The README's counter has three parts. First the state and messages:

```rust
#[derive(Default)]
struct Counter {
    value: i32,
}

#[derive(Debug, Clone, Copy)]
pub enum Message {
    Increment,
    Decrement,
}
```

Then the view method, which returns a column of widgets. The README writes it as:

```rust
use iced::widget::{button, column, text, Column};

impl Counter {
    pub fn view(&self) -> Column<'_, Message> {
        column![
            button("+").on_press(Message::Increment),
            text(self.value).size(50),
            button("-").on_press(Message::Decrement),
        ]
    }
}
```

Finally the update method and main. The README's entry point is a single call:

```rust
fn main() -> iced::Result {
    iced::run(Counter::update, Counter::view)
}
```

Running the counter should open a window with a number and two buttons. The README also points to the book at book.iced.rs, the API documentation at docs.rs/iced, and the examples directory for more complete programs.

## What the experimental label actually costs you

The release history is the first concrete constraint. The recent releases listed are 0.14.0 on 2025-12-07, 0.13.1 on 2024-09-19 and 0.13.0 on 2024-09-18. That is roughly fifteen months between the 0.13 line and 0.14.0. A library that ships breaking changes on that cadence is a library you plan around, not a library you pin and forget.

The README's own feature list flags the riskiest parts. Debug tooling with performance metrics and time traveling is offered, but the Cargo.toml comments mark time-travel as "very experimental" and hot reloading as "very experimental" as well. If your workflow depends on hot reload, you are building on the least settled corner of the project.

There is also a scope limit. Iced is a GUI library, not an application framework. It has no built-in persistence, networking or routing. Async actions are supported through futures, but you supply the async runtime decisions. For a data-heavy desktop tool with tables, tree views and native menus, the built-in widget set in the README (text inputs, scrollables, buttons, canvas, markdown, qr_code and others behind features) may not cover what you need, and custom widgets mean writing your own layout and drawing logic.

The last push to the repository was on 2026-09-10, so the project is not archived and not dormant. Activity on master does not change the API stability picture, though. A commit stream and a stable release cadence are different things.

## Iced against egui: retained messages versus immediate mode

The closest comparison in the Rust GUI space is egui, an immediate-mode library. The difference is architectural, not cosmetic.

In egui, you write a function that runs every frame and directly mutates your state through closures. There is no message enum and no update function; the UI code and the state mutation sit in the same place. In Iced, the view function is pure. It returns widgets that carry messages, and all mutation happens in update. That separation is what makes the Elm model testable: you can call update with a message and assert on the resulting state without opening a window.

The trade-off runs the other way too. Immediate mode is simpler to start with because there is no loop to learn, and it adapts well to interfaces that change shape every frame. Iced's retained widget tree and message plumbing add ceremony for a small tool. Iced also carries a heavier rendering stack by default, since the default features pull in both wgpu and tiny-skia, while an immediate-mode library can be lighter. If your interface is a quick internal panel, egui will get you there with less structure. If your interface has state transitions you want to reason about and test, Iced's message enum is the better fit.

## Maintenance, licence and the upgrade path

Iced is MIT licensed, per the repository's LICENSE file and the Cargo.toml licence field inherited from the workspace. MIT is permissive: you can use it in closed-source software, and the main obligation is preserving the copyright notice. That is a statement about the licence text, not legal advice for your situation.

The maintenance signal is mixed but readable. The repository is not archived, and the last push was on 2026-09-10. The Cargo.toml even declares a badge with maintenance status "actively-developed". Against that, the README's own warning about experimental software and the gap between 0.13.1 and 0.14.0 mean upgrade cost is real. Pre-1.0 versioning gives the maintainers room to break APIs, and the CHANGELOG.md at the repository root is where those breaks are recorded.

Budget for upgrades as a recurring task. Pin an exact version in Cargo.toml rather than a caret range if you want to control when a break lands, read CHANGELOG.md before moving, and check ROADMAP.md to see whether the areas you depend on are slated for rework. The repository also carries a DEPENDENCIES.md file, which is worth reading if your organisation audits transitive crates.

## Where Iced is the wrong tool

Three cases stand out. The first is a product that must ship on a fixed schedule with a frozen API. A pre-1.0 library with a fifteen-month release gap is a poor foundation for that, no matter how clean the architecture is.

The second is an application that is mostly native platform chrome: system tray integration, native menus, file dialogs, accessibility trees. The README's feature list covers widgets and rendering, not platform integration depth, and the windowing shell is listed as a component rather than a full platform abstraction.

The third is a team without Rust experience. The Elm loop is not hard, but the borrow-checker interactions in view functions that return Column<'_, Message> require comfort with lifetimes. The README's example is deliberately small; real applications will hit the parts the README does not show.

## Conclusion

Adopt Iced if you are building a Rust application whose interface is mostly built-in widgets and you want the Elm loop to keep state changes explicit. Do not adopt it if you need a stable API across releases or a mature widget set, because the README itself labels the project experimental and the release history shows long gaps between versions. Before writing application code, run the counter example, and read the ROADMAP.md and CHANGELOG.md entries for the version you are pinning. That tells you which side of the experimental label you are buying into.

## FAQ

### Is Iced cross-platform?

Yes. The README lists cross-platform support for Windows, macOS, Linux and the Web. The default Cargo.toml features enable the wgpu renderer with Vulkan, Metal, DX12, OpenGL and WebGPU backends, plus x11 and wayland on Linux.

### Is there a cross-platform GUI framework for Rust?

Iced is one: a cross-platform GUI library for Rust focused on simplicity and type-safety, inspired by Elm. The README lists Windows, macOS, Linux and the Web as supported targets.

### How to build a GUI in Rust with Iced?

Model your application as state, messages, view logic and update logic, then call iced::run with your update and view functions as the README's counter example does. The README points to the book, the docs.rs documentation and the examples directory for more.

### What is the best GUI framework for Rust?

The README does not rank Rust GUI frameworks. Iced describes itself as experimental software, so the choice depends on whether you want the Elm-style message loop and can absorb pre-1.0 API changes.

## Sources

- [iced-rs/iced on GitHub](https://github.com/iced-rs/iced)
- [License: MIT](https://github.com/iced-rs/iced/blob/master/LICENSE)
- [Project website](https://iced.rs)
- [README](https://github.com/iced-rs/iced/blob/master/README.md)
- [Releases](https://github.com/iced-rs/iced/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/iced-rs-iced
