Top Related Projects
An opinionated 2D game engine for Rust
egui: an easy-to-use immediate mode GUI in Rust that runs on both web and native
Rust bindings for the FLTK GUI library.
Slint is a declarative GUI toolkit to build native user interfaces for Rust, C++, or JavaScript apps.
The Rust UI-Toolkit.
A data-first Rust-native UI design toolkit.
Quick Overview
Iced is a cross-platform GUI library for Rust, designed to be simple, fast, and extensible. It provides a declarative API for building user interfaces, allowing developers to create responsive and visually appealing applications for desktop, web, and mobile platforms.
Pros
- Cross-Platform: Iced supports multiple platforms, including Windows, macOS, Linux, and WebAssembly, allowing developers to create a single codebase for their applications.
- Declarative API: Iced's declarative API makes it easy to build and maintain complex user interfaces, as developers can focus on describing the desired state of the UI rather than managing low-level details.
- Performance: Iced is designed to be fast and efficient, with a focus on minimizing resource usage and providing smooth animations.
- Extensibility: Iced is built with extensibility in mind, allowing developers to create custom widgets, layouts, and event handlers to suit their specific needs.
Cons
- Learning Curve: Iced's declarative approach may require a learning curve for developers who are more familiar with imperative UI frameworks.
- Limited Ecosystem: Compared to some other GUI frameworks, Iced has a smaller ecosystem of third-party libraries and tools, which may limit the availability of pre-built components and features.
- Maturity: Iced is a relatively new project, and while it is actively developed, it may not have the same level of stability and feature completeness as more established GUI frameworks.
- Performance Concerns: While Iced is designed to be performant, some users have reported performance issues, particularly on older or less powerful hardware.
Code Examples
Here are a few examples of how to use Iced:
- Creating a Simple Window:
use iced::{Application, Command, Element, Settings, Subscription, Text, Window};
struct MyApp;
impl Application for MyApp {
type Executor = iced::executor::Default;
type Message = ();
type Flags = ();
fn new(_flags: ()) -> (Self, Command<Self::Message>) {
(MyApp, Command::none())
}
fn title(&self) -> String {
String::from("My Iced App")
}
fn update(&mut self, _message: Self::Message) -> Command<Self::Message> {
Command::none()
}
fn view(&self) -> Element<Self::Message> {
Text::new("Hello, Iced!").into()
}
}
fn main() {
MyApp::run(Settings {
window: Window::new(),
..Settings::default()
})
}
- Creating a Button:
use iced::{button, Application, Button, Command, Element, Settings, Subscription, Text, Window};
struct MyApp {
button_state: button::State,
}
impl Application for MyApp {
// ... (same as previous example)
fn new(_flags: ()) -> (Self, Command<Self::Message>) {
(MyApp { button_state: button::State::new() }, Command::none())
}
fn update(&mut self, _message: Self::Message) -> Command<Self::Message> {
Command::none()
}
fn view(&self) -> Element<Self::Message> {
Button::new(&mut self.button_state, Text::new("Click me!"))
.on_press(Command::none())
.into()
}
}
fn main() {
MyApp::run(Settings {
window: Window::new(),
..Settings::default()
})
}
- Handling User Input:
use iced::{
button, text_input, Application, Button, Command, Element, Settings, Subscription, Text,
TextInput, Window,
};
struct MyApp {
button_state: button::State,
text_input_state: text_input::State,
input_value: String,
}
impl Application for MyApp {
// ... (same as previous examples)
fn new(_flags: ()) -> (Self, Command<Self::Message>) {
(
MyApp {
button
Competitor Comparisons
An opinionated 2D game engine for Rust
Pros of Coffee
- Simpler API with fewer abstractions, making it easier to get started
- More focused on 2D graphics and game development
- Provides built-in support for audio playback
Cons of Coffee
- Less comprehensive widget system compared to Iced
- Smaller community and ecosystem
- Limited cross-platform support (primarily targets desktop platforms)
Code Comparison
Coffee example:
use coffee::graphics::{Color, Frame, Window};
use coffee::load::Task;
use coffee::{Game, Result, Timer};
struct MyGame;
impl Game for MyGame {
fn draw(&mut self, frame: &mut Frame) {
frame.clear(Color::BLACK);
}
}
Iced example:
use iced::{Element, Sandbox, Settings};
struct MyApp;
impl Sandbox for MyApp {
type Message = ();
fn new() -> Self {
MyApp
}
fn view(&mut self) -> Element<Self::Message> {
"Hello, Iced!".into()
}
}
Both Coffee and Iced are Rust libraries for building user interfaces, but they have different focuses. Coffee is more oriented towards 2D graphics and game development, while Iced provides a more comprehensive framework for building general-purpose GUI applications. Iced offers a richer set of widgets and better cross-platform support, making it more suitable for complex desktop applications. Coffee, on the other hand, has a simpler API and built-in audio support, which can be advantageous for smaller projects or games.
egui: an easy-to-use immediate mode GUI in Rust that runs on both web and native
Pros of egui
- Immediate mode GUI, offering simpler state management and faster prototyping
- Lightweight and easy to integrate into existing projects
- Supports both native and web targets with a single codebase
Cons of egui
- Less customizable appearance compared to retained mode GUIs
- May have performance limitations for complex UIs with many elements
- Lacks some advanced features found in more mature GUI frameworks
Code Comparison
egui:
ui.heading("My egui App");
ui.horizontal(|ui| {
if ui.button("Click me").clicked() {
println!("Button clicked!");
}
ui.label("Hello, world!");
});
iced:
Column::new()
.push(Text::new("My iced App"))
.push(Button::new("Click me").on_press(Message::ButtonClicked))
.push(Text::new("Hello, world!"))
.into()
Both egui and iced are Rust GUI libraries, but they follow different paradigms. egui uses immediate mode rendering, which can be more intuitive for simple applications, while iced uses a more traditional retained mode approach with a Elm-like architecture. The choice between them depends on the specific requirements of your project and personal preference.
Rust bindings for the FLTK GUI library.
Pros of fltk-rs
- Mature and stable, based on the well-established FLTK C++ library
- Lightweight with minimal dependencies
- Cross-platform support with native look and feel
Cons of fltk-rs
- Less idiomatic Rust API compared to Iced
- Limited built-in widgets and customization options
- Steeper learning curve for Rust developers unfamiliar with FLTK
Code Comparison
fltk-rs:
use fltk::{app, button::Button, frame::Frame, prelude::*, window::Window};
fn main() {
let app = app::App::default();
let mut wind = Window::new(100, 100, 400, 300, "Hello from fltk-rs");
let mut frame = Frame::new(0, 0, 400, 200, "");
let mut but = Button::new(160, 210, 80, 40, "Click me!");
wind.end();
wind.show();
but.set_callback(move |_| frame.set_label("Hello World!"));
app.run().unwrap();
}
Iced:
use iced::{button, Button, Column, Element, Sandbox, Settings, Text};
struct Counter {
value: i32,
increment_button: button::State,
}
impl Sandbox for Counter {
type Message = Message;
// ... (implementation details omitted for brevity)
}
fn main() -> iced::Result {
Counter::run(Settings::default())
}
Slint is a declarative GUI toolkit to build native user interfaces for Rust, C++, or JavaScript apps.
Pros of Slint
- Cross-platform support for desktop and embedded systems
- Declarative UI design with a custom markup language
- Built-in support for multiple programming languages (Rust, C++, JavaScript)
Cons of Slint
- Smaller community and ecosystem compared to Iced
- Steeper learning curve due to custom markup language
- Less mature and fewer real-world applications
Code Comparison
Slint (using .slint markup):
export component Button {
Text {
text: "Click me!";
}
}
Iced (using Rust):
use iced::widget::Button;
let button = Button::new("Click me!");
Slint focuses on a declarative approach with its custom markup language, while Iced uses a more traditional Rust-based approach for defining UI elements. Slint's syntax may be more intuitive for designers, while Iced's approach might be more familiar to Rust developers.
Both frameworks aim to provide cross-platform GUI development for Rust, but they take different approaches. Slint offers broader language support and a unique markup language, while Iced focuses on a pure Rust implementation with a more traditional API. The choice between them depends on specific project requirements and developer preferences.
The Rust UI-Toolkit.
Pros of OrbTk
- Cross-platform support for desktop, web, and mobile
- Integrated layout system with flexbox-like capabilities
- More mature project with a longer development history
Cons of OrbTk
- Less active development and community engagement
- Steeper learning curve due to more complex architecture
- Limited documentation and examples compared to Iced
Code Comparison
OrbTk example:
use orbtk::prelude::*;
fn main() {
Application::new()
.window(|ctx| {
Window::new()
.title("OrbTk Example")
.child(TextBlock::new().text("Hello, OrbTk!").build(ctx))
.build(ctx)
})
.run();
}
Iced example:
use iced::{Element, Sandbox, Settings, Text};
struct HelloWorld;
impl Sandbox for HelloWorld {
type Message = ();
fn new() -> Self { HelloWorld }
fn title(&self) -> String { String::from("Iced Example") }
fn view(&self) -> Element<Self::Message> { Text::new("Hello, Iced!").into() }
fn update(&mut self, _message: Self::Message) {}
}
fn main() -> iced::Result {
HelloWorld::run(Settings::default())
}
Both frameworks offer declarative UI design, but OrbTk uses a more widget-centric approach, while Iced employs a more functional style with its Sandbox
trait. Iced's code tends to be more concise and straightforward for simple applications, whereas OrbTk provides more granular control over widget properties and layout.
A data-first Rust-native UI design toolkit.
Pros of Druid
- More mature and feature-rich, with a larger ecosystem of widgets and components
- Better support for complex layouts and custom drawing
- Stronger focus on data-driven design, making it easier to manage application state
Cons of Druid
- Steeper learning curve due to its more complex architecture
- Less idiomatic Rust code, with more use of macros and dynamic typing
- Slower development pace and less frequent updates compared to Iced
Code Comparison
Iced:
use iced::{button, Button, Column, Element, Sandbox, Settings, Text};
struct Counter {
value: i32,
increment_button: button::State,
decrement_button: button::State,
}
Druid:
use druid::widget::{Button, Flex, Label};
use druid::{AppLauncher, Data, Lens, Widget, WindowDesc};
#[derive(Clone, Data, Lens)]
struct CounterState {
count: i32,
}
Both Iced and Druid are GUI frameworks for Rust, aiming to provide declarative and efficient ways to build user interfaces. Iced offers a simpler, more straightforward approach with a focus on ease of use and idiomatic Rust code. Druid, on the other hand, provides more advanced features and flexibility at the cost of increased complexity. The choice between the two depends on the specific requirements of your project and your familiarity with Rust GUI development.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Features
- Simple, easy-to-use, batteries-included API
- Type-safe, reactive programming model
- Cross-platform support (Windows, macOS, Linux, and the Web)
- Responsive layout
- Built-in widgets (including text inputs, scrollables, and more!)
- Custom widget support (create your own!)
- Debug overlay with performance metrics
- First-class support for async actions (use futures!)
- Modular ecosystem split into reusable parts:
- A renderer-agnostic native runtime enabling integration with existing systems
- Two built-in renderers leveraging
wgpu
andtiny-skia
iced_wgpu
supporting Vulkan, Metal and DX12iced_tiny_skia
offering a software alternative as a fallback
- A windowing shell
- A web runtime leveraging the DOM
Iced is currently experimental software. Take a look at the roadmap, check out the issues, and feel free to contribute!
Overview
Inspired by The Elm Architecture, Iced expects you to split user interfaces into four different concepts:
- State â the state of your application
- Messages â user interactions or meaningful events that you care about
- View logic â a way to display your state as widgets that may produce messages on user interaction
- Update logic â a way to react to messages and update your state
We can build something to see how this works! Let's say we want a simple counter that can be incremented and decremented using two buttons.
We start by modelling the state of our application:
#[derive(Default)]
struct Counter {
value: i32,
}
Next, we need to define the possible user interactions of our counter: the button presses. These interactions are our messages:
#[derive(Debug, Clone, Copy)]
pub enum Message {
Increment,
Decrement,
}
Now, let's show the actual counter by putting it all together in our view logic:
use iced::widget::{button, column, text, Column};
impl Counter {
pub fn view(&self) -> Column<Message> {
// We use a column: a simple vertical layout
column![
// The increment button. We tell it to produce an
// `Increment` message when pressed
button("+").on_press(Message::Increment),
// We show the value of the counter here
text(self.value).size(50),
// The decrement button. We tell it to produce a
// `Decrement` message when pressed
button("-").on_press(Message::Decrement),
]
}
}
Finally, we need to be able to react to any produced messages and change our state accordingly in our update logic:
impl Counter {
// ...
pub fn update(&mut self, message: Message) {
match message {
Message::Increment => {
self.value += 1;
}
Message::Decrement => {
self.value -= 1;
}
}
}
}
And that's everything! We just wrote a whole user interface. Let's run it:
fn main() -> iced::Result {
iced::run("A cool counter", Counter::update, Counter::view)
}
Iced will automatically:
- Take the result of our view logic and layout its widgets.
- Process events from our system and produce messages for our update logic.
- Draw the resulting user interface.
Read the book, the documentation, and the examples to learn more!
Implementation details
Iced was originally born as an attempt at bringing the simplicity of Elm and The Elm Architecture into Coffee, a 2D game engine I am working on.
The core of the library was implemented during May 2019 in this pull request.
The first alpha version was eventually released as
a renderer-agnostic GUI library. The library did not provide a renderer and
implemented the current tour example on top of ggez
, a game library.
Since then, the focus has shifted towards providing a batteries-included, end-user-oriented GUI library, while keeping the ecosystem modular:
Contributing / Feedback
Contributions are greatly appreciated! If you want to contribute, please read our contributing guidelines for more details.
Feedback is also welcome! You can create a new topic in our Discourse forum or come chat to our Discord server.
Sponsors
The development of Iced is sponsored by the Cryptowatch team at Kraken.com
Top Related Projects
An opinionated 2D game engine for Rust
egui: an easy-to-use immediate mode GUI in Rust that runs on both web and native
Rust bindings for the FLTK GUI library.
Slint is a declarative GUI toolkit to build native user interfaces for Rust, C++, or JavaScript apps.
The Rust UI-Toolkit.
A data-first Rust-native UI design toolkit.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot