Convert Figma logo to code with AI

iced-rs logoiced

A cross-platform GUI library for Rust, inspired by Elm

24,482
1,140
24,482
284

Top Related Projects

1,073

An opinionated 2D game engine for Rust

22,057

egui: an easy-to-use immediate mode GUI in Rust that runs on both web and native

1,601

Rust bindings for the FLTK GUI library.

17,220

Slint is a declarative GUI toolkit to build native user interfaces for Rust, C++, or JavaScript apps.

3,768

The Rust UI-Toolkit.

9,491

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:

  1. 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()
    })
}
  1. 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()
    })
}
  1. 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

1,073

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.

22,057

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.

1,601

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())
}
17,220

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.

3,768

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.

9,491

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 Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Iced

Documentation Crates.io License Downloads Test Status Discourse Discord Server

A cross-platform GUI library for Rust focused on simplicity and type-safety. Inspired by Elm.

Features

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:

  1. Take the result of our view logic and layout its widgets.
  2. Process events from our system and produce messages for our update logic.
  3. 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:

The Iced Ecosystem

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