Advanced 16 min

Write clean Argui code

Keep state authoritative, views readable, updates bounded, and feature costs visible.

Prefer explicit data flow

Use plain structs and enums, short event routes, and view functions named after product concepts. Keep one authoritative value for each piece of state and derive presentation values during render.

  • Stable domain keys for interactive or reorderable elements
  • No I/O or unbounded allocation in render
  • No speculative abstractions or generic utils modules
  • Feature flags only for integrations the application actually uses

Invalidate with intent

Call notify after state changes that affect a presentation. Keep paint-only changes as paint properties and avoid rebuilding ownership graphs for temporary visual states. Measure before introducing caches.

Test behavior at the right boundary

Test state transitions, invalidation class, layout results, semantics, and event translation through public behavior. Browser checks must verify both pixels and semantic controls; native GUI checks run on a private display.

Reference files

These are the implementation and guide files used for this chapter.

docs/contributing/code-quality.mdcrates/argui-widget-gallery/src/pages/data_table.rsapp_examples/fake-ai-harness/src/app/view.rs
Compiled example

A larger feature split into focused state and view code

The Rust file below is imported verbatim by this page and compiled into the WebAssembly application running underneath it.

Open exact source
app_examples/docs-examples/src/examples/clean_code.rs
use argui::{
    paint::{Border, CornerRadii},
    runtime::{Context, Render},
    text::TextStyle,
    ui::{AlignItems, Element, Sides, length, percent},
    widgets::{Button, default_theme},
};

const CHECKS: [&str; 3] = [
    "State is explicit",
    "Views stay pure",
    "Effects have owners",
];

#[derive(Default)]
pub struct Example {
    completed: usize,
}

impl Render for Example {
    fn render(&mut self, cx: &mut Context<Self>) -> Element {
        let themes = default_theme(cx.environment());
        let theme = themes.resolve(cx.environment().color_scheme);
        let rows = CHECKS.into_iter().enumerate().map(|(index, label)| {
            let complete = index < self.completed;
            Element::row([
                Element::text(if complete { "Done" } else { "Pending" })
                    .width(length(68.0))
                    .text_style(TextStyle {
                        color: if complete {
                            theme.primary
                        } else {
                            theme.muted_foreground
                        },
                        font_size: 12.0,
                        weight: 700,
                        ..TextStyle::default()
                    }),
                Element::text(label).text_style(TextStyle {
                    color: theme.foreground,
                    ..TextStyle::default()
                }),
            ])
            .width(percent(1.0))
            .align_items(AlignItems::CENTER)
            .padding(Sides::length(12.0))
            .gap(12.0)
            .background(theme.card)
            .border(Border::all(1.0, theme.border))
            .radius(CornerRadii::all(8.0))
        });
        let next_label = if self.completed == CHECKS.len() {
            "All complete".to_owned()
        } else {
            format!("Complete next ({} of {})", self.completed + 1, CHECKS.len())
        };
        Element::column(
            rows.chain([Button::new("next", next_label, theme.button())
                .enabled(self.completed < CHECKS.len())
                .on_click(cx.callback(|app| {
                    app.completed = (app.completed + 1).min(CHECKS.len());
                }))
                .build()]),
        )
        .width(percent(1.0))
        .height(percent(1.0))
        .padding(Sides::length(28.0))
        .gap(14.0)
        .background(theme.background)
    }
}