0Pricing
Learn Rust Coding · Lesson

Immediate-Mode UI Basics

Understand how egui renders.

Immediate-Mode UI Basics is a free Learn Rust Coding lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Learn Rust Coding learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Immediate Mode Means

egui is an immediate-mode GUI library. Unlike retained-mode toolkits (GTK, Qt) where you build a persistent widget tree, egui rebuilds the entire UI from scratch on every frame.

Your code runs top-to-bottom each frame, emitting widgets as function calls. There is no stored node graph — the UI is just a side effect of running your closure.

The Update Loop

An egui app implements eframe::App, whose update method is called once per frame, typically 60 times a second.

Inside update you describe what the UI should look like right now, given current state. egui diffs nothing — it simply re-emits everything.

impl eframe::App for MyApp {
    fn update(&mut self, ctx: &egui::Context, _f: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            ui.label("Hello, egui!");
        });
    }
}

Context and Ui

Two core types appear everywhere. egui::Context is the shared, cheaply-cloneable handle to the whole UI state for the frame.

Ui is a region you add widgets into. Panels and windows hand you a &mut Ui inside their closure; you call methods like ui.label or ui.button on it.

egui::CentralPanel::default().show(ctx, |ui| {
    ui.heading("Dashboard");
    ui.separator();
    ui.label("Widgets go here.");
});

Widgets Return Responses

Because the UI is rebuilt each frame, you cannot register callbacks the way retained toolkits do. Instead, every widget call returns a Response immediately.

You inspect the Response in the same frame: .clicked(), .hovered(), .changed(). This keeps event handling inline with layout.

if ui.button("Save").clicked() {
    self.save();
}
let resp = ui.text_edit_singleline(&mut self.name);
if resp.changed() {
    self.dirty = true;
}

State Lives in You

egui stores almost no application data. Your widget values live in self on the App struct, and you pass mutable references into widgets.

A text field takes &mut String; a checkbox takes &mut bool. egui writes back through the reference, so your struct is the single source of truth.

struct MyApp {
    name: String,
    enabled: bool,
}

// inside update:
ui.text_edit_singleline(&mut self.name);
ui.checkbox(&mut self.enabled, "Enabled");

Setting Up eframe

eframe is the official framework that hosts egui on desktop and web. It owns the native window, the GPU surface, and the event loop.

You launch with eframe::run_native, passing a window title, native options, and a closure that builds your App.

fn main() -> eframe::Result<()> {
    let opts = eframe::NativeOptions::default();
    eframe::run_native(
        "My App",
        opts,
        Box::new(|_cc| Ok(Box::new(MyApp::default()))),
    )
}

Repaint Driven by Input

By default egui only repaints when something happens: mouse move, key press, or an explicit request. This saves CPU on idle apps.

For animations or live data, call ctx.request_repaint() to schedule another frame, or request_repaint_after with a duration for timed updates.

ui.label(format!("Tick: {}", self.counter));
self.counter += 1;
ctx.request_repaint(); // keep animating

No Retained Widget IDs (Mostly)

egui derives widget identity from call order and position, generating an Id automatically. This matters for things like collapsing headers that need stable memory between frames.

When two widgets would collide — e.g. buttons with the same label in a loop — wrap them with ui.push_id(i, |ui| ...) to disambiguate.

for (i, item) in self.items.iter().enumerate() {
    ui.push_id(i, |ui| {
        if ui.button(&item.label).clicked() {
            println!("clicked {}", i);
        }
    });
}

Immediate Mode Trade-offs

Immediate mode makes UI code trivially data-driven: an if shows or hides a section, a for renders a list. There is no manual add/remove of nodes and no desync between model and view.

The cost is that the whole UI re-runs each frame, so keep per-frame work cheap and avoid heavy computation inside update.

if self.logged_in {
    ui.label(format!("Welcome, {}", self.user));
} else {
    ui.label("Please sign in.");
}

Minimal Complete App

Putting it together: a struct holds state, update draws it, and main runs the window. This is the entire skeleton of any egui desktop program.

Everything else — layout, theming, packaging — builds on this loop.

#[derive(Default)]
struct App { count: i32 }

impl eframe::App for App {
    fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
        egui::CentralPanel::default().show(ctx, |ui| {
            if ui.button("+1").clicked() { self.count += 1; }
            ui.label(format!("Count: {}", self.count));
        });
    }
}

Why Rust Loves egui

egui's design plays to Rust's strengths. Borrowing &mut self into a single update call means the borrow checker guarantees no data races between widgets and your model.

There are no callbacks capturing shared mutable state, no Rc<RefCell> graphs forced by a retained tree — just plain ownership and one mutable pass per frame.

Quick Check

Test your grasp of immediate mode.

Recap

egui is immediate-mode: the whole UI is rebuilt each frame inside eframe::App::update. Widgets are function calls that return a Response you inspect right away.

Your data lives in self and is passed by mutable reference, so the borrow checker keeps model and view in sync. eframe::run_native hosts the window and event loop. Next we explore the widget palette and layout system.

Frequently asked questions

Is the “Immediate-Mode UI Basics” lesson free?

Yes — the full text of “Immediate-Mode UI Basics” is free to read here on the web, and the Learn Rust Coding course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Learn Rust Coding course, upgrade to CoddyKit PRO.

What will I learn in “Immediate-Mode UI Basics”?

Understand how egui renders. You practise Learn Rust Coding with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Learn Rust Coding?

No prior experience is required. Learn Rust Coding on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Immediate-Mode UI Basics” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Learn Rust Coding lesson?

Yes. Every Learn Rust Coding lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Immediate-Mode UI Basics
  2. Widgets and Layout
  3. Managing App State
  4. Packaging a Desktop App
← Back to Learn Rust Coding