Skip to main content
Let’s build your first Iced application! You’ll create a simple counter with increment and decrement buttons to learn the fundamentals of Iced’s architecture.
Make sure you have Rust installed before starting. You’ll need Rust 1.92 or later.

What You’ll Build

A counter application that displays a number and two buttons to increment and decrement it. This demonstrates Iced’s core concepts: state management, message handling, and reactive UI updates.
1

Create a new project

Create a new Rust binary project:
cargo new counter
cd counter
2

Add Iced dependency

Open Cargo.toml and add Iced:
Cargo.toml
[package]
name = "counter"
version = "0.1.0"
edition = "2021"

[dependencies]
iced = "0.15.0-dev"
The default features include the wgpu renderer and thread-pool executor, which work for most applications. See Installation for other configurations.
3

Define your application state

Open src/main.rs and define the state of your application. For a counter, you only need to track a single number:
src/main.rs
#[derive(Default)]
struct Counter {
    value: i64,
}
The Default trait allows Iced to initialize your application with Counter { value: 0 }.
4

Define your messages

Messages represent the events your application can handle. For a counter, you need increment and decrement actions:
src/main.rs
#[derive(Debug, Clone, Copy)]
enum Message {
    Increment,
    Decrement,
}
5

Implement the update logic

The update method handles messages and modifies your application state:
src/main.rs
impl Counter {
    fn update(&mut self, message: Message) {
        match message {
            Message::Increment => {
                self.value += 1;
            }
            Message::Decrement => {
                self.value -= 1;
            }
        }
    }
}
6

Implement the view logic

The view method describes how your application should look. It returns a layout of widgets that can produce messages:
src/main.rs
use iced::widget::{button, column, text, Column};
use iced::Center;

impl Counter {
    fn view(&self) -> Column<'_, Message> {
        column![
            button("Increment").on_press(Message::Increment),
            text(self.value).size(50),
            button("Decrement").on_press(Message::Decrement)
        ]
        .padding(20)
        .align_x(Center)
    }
}
The column! macro creates a vertical layout. Each button produces a message when pressed, and the text widget displays the current counter value.
7

Run your application

Add the main function to run your application:
src/main.rs
pub fn main() -> iced::Result {
    iced::run(Counter::update, Counter::view)
}
iced::run starts your application by taking your update and view functions. It automatically:
  • Creates a window
  • Renders your UI
  • Processes user interactions
  • Calls your update method when buttons are clicked
  • Re-renders when state changes
8

Build and run

Run your application:
cargo run
You should see a window with your counter! Click the buttons to see the number change.

Complete Code

Here’s the full src/main.rs file:
src/main.rs
use iced::widget::{button, column, text, Column};
use iced::Center;

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

#[derive(Default)]
struct Counter {
    value: i64,
}

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

impl Counter {
    fn update(&mut self, message: Message) {
        match message {
            Message::Increment => {
                self.value += 1;
            }
            Message::Decrement => {
                self.value -= 1;
            }
        }
    }

    fn view(&self) -> Column<'_, Message> {
        column![
            button("Increment").on_press(Message::Increment),
            text(self.value).size(50),
            button("Decrement").on_press(Message::Decrement)
        ]
        .padding(20)
        .align_x(Center)
    }
}

Understanding the Architecture

This counter demonstrates The Elm Architecture that Iced follows:
  1. State (Counter) - Holds your application data
  2. Messages (Message) - Represent events that can change state
  3. Update logic (update) - Handles messages and modifies state
  4. View logic (view) - Renders UI based on current state
When you click a button:
  1. The button produces a Message::Increment or Message::Decrement
  2. Iced calls your update method with that message
  3. Your update method modifies the state
  4. Iced calls your view method to re-render the UI
  5. You see the updated counter value
This unidirectional data flow makes your application predictable and easy to reason about.

Next Steps

Now that you’ve built your first Iced application, explore more concepts:

Architecture

Deep dive into The Elm Architecture and how Iced works

Widgets

Learn about available widgets and how to use them

Styling

Customize the appearance of your application

Tasks

Handle async operations with tasks
Enable debug mode to see performance metrics and inspect your application. Add the debug feature to your Cargo.toml and press F12 while running your app.

Build docs developers (and LLMs) love