Note: Update (April 25, 2025) This blog uses Iced 0.14.0 - The Iced repo updates often and might come with breaking changes each version. Get the code here.
Iced and The Elm Architecture
Iced structured its design from The Elm Architecture, which basically just follows a loop of retrieving the state of the application, updating it, then displaying it. Specifically, it follows this diagram where:
- Model: The shape of your app (widgets, bools, structs, etc.)
struct Circle{
radius: f32,
}
- Messages: Some sort of event or action
enum Message{
IncreaseRadius,
}
- Update: based off of the messages we defined, do something as a result
fn update(&mut self, message: Message) -> Task<Message>{
match message {
Message::IncreaseRadius => {
self.circle.radius += 5.0;
}
}
Task::none()
}
The overall structure is a bit different then other GUI librariers like Qt, but the design does get intuitive once you start getting into it.
At the most basic level:

Monolithic structures creates confusing code
When I was looking around for some example code and tutorials, I noticed a trend of most examples taking a monolithic approach to building applications. In other words, every single state, model, message, update, view are added inside of the main.rs file.
After doing some personal projects using Iced using this method, I noticed that my own code started to become confusing and unmanagable really quickly. Even a relatively simple app can span over a hundred messages, and updates can be 1000's of lines of code. This is on top of your view() function where you will layout the look of the app, which adds even more complexity to your main.rs file.
After my own struggles working with my monolithic code structure, I wanted to take a look at some other popular large Iced projects. I took a lot of inspiration from Halloy and how they structured their UI into seperate widgets and components.
Taking advantage of Iced's architecture
Iced is already set up for the most part to contain its model, messages, updates into its own application/component. Therefore, it is reasonable to also split some logic into seperate "modules". For example, splitting this:
//main.rs
enum Message{
IncreaseRadius,
DecreaseRadius,
DrawCircle,
DrawText,
}
into a more specific context given from some other component file:
//main.rs
enum Message{
DrawCricle,
DrawText,
}
//circle_widget.rs
enum Message{
IncreaseRadius,
DecreaseRadius,
}