Intermediate 18 min

The Argui mental model

Understand models, presentations, retained elements, layout, paint, semantics, and host boundaries.

What Argui is

Argui is a retained, GPU-rendered application UI runtime written in Rust. Application models own state and render cloneable Element descriptions; the runtime reconciles those descriptions into persistent presentation nodes, computes layout, paints through WGPU, and publishes an accessibility tree.

It is designed for product interfaces that need explicit state, native input, deterministic updates, portable rendering, and opt-in platform integrations without a browser DOM as the primary runtime.

Retained versus immediate mode

Immediate-mode UI code describes and processes the interface afresh for each frame. Argui view code also returns a description, but the runtime retains the resulting nodes between renders and reconciles only what changed. Stable identity therefore matters: it preserves focus, scrolling, handlers, accessibility state, animation, and cached layout or paint work.

ConcernImmediate modeArgui retained mode
LifetimeRecreated as part of each frameNodes persist until reconciliation removes them
StateOften coupled to the frame loopOwned explicitly by application models
UpdatesFrame-orientedClassified as semantic, paint, scroll, or layout work
IdentityUsually positional or call-site basedStable keys and model-owned handler slots
Idle costCommonly redraws continuouslyRequests frames only when work is pending

What Argui is not

Argui does not hide application state inside widgets, generate a web DOM for native targets, or make every operating-system service portable by pretending platform differences do not exist.

  • Not an immediate-mode frame loop: view descriptions reconcile into retained nodes.
  • Not an HTML/CSS wrapper: layout, text, paint, interaction, and semantics are Rust-native layers.
  • Not a business-state store: widgets remain controlled by the owning application model.
  • Not a universal native-services abstraction: shared contracts stay small and platform adapters remain explicit.
  • Not a replacement for device and renderer testing: headless tests cover behavior, while browser and native checks cover integration.

Data flows through explicit layers

A model renders an Element description. The retained UI reconciles stable nodes, Taffy computes logical geometry, Cosmic Text shapes glyphs, paint records renderer-neutral primitives, and WGPU submits the final frame. Accessibility consumes a parallel semantic tree.

Pipeline
model Element tree retained update
                    ├→ layout shaped text paint WGPU
                    └→ semantics AccessKit / browser DOM

Not every change costs a layout

UiTree classifies updates as none, semantics, paint, scroll, or layout. A color change can reuse geometry and shaped text; a semantic-only change can avoid GPU submission; an unchanged rebuild performs no presentation work.

Separate model data from presentations

Entity retains model data and model-owned resources. Mount represents one presentation with its own environment, cache, handlers, dependencies, and view tasks. One model may therefore appear in multiple windows without merging their presentation state.

Reference files

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

docs/architecture.mddocs/runtime/models.mddocs/rendering/primitives.md
Compiled example

The retained pipeline in a running application

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/mental_model.rs
use argui::{
    paint::{Border, CornerRadii},
    runtime::{Context, Render},
    text::TextStyle,
    ui::{Element, FlexWrap, Sides, length, percent},
    widgets::default_theme,
};

pub struct Example;

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 stage = |label| {
            Element::text(label)
                .text_style(TextStyle {
                    color: theme.primary_foreground,
                    weight: 650,
                    ..TextStyle::default()
                })
                .width(length(150.0))
                .grow(1.0)
                .padding(Sides::length(18.0))
                .background(theme.primary)
                .border(Border::all(1.0, theme.ring))
                .radius(CornerRadii::all(10.0))
        };
        Element::row([
            stage("Model state"),
            stage("Element tree"),
            stage("Layout + text"),
            stage("WGPU paint"),
        ])
        .width(percent(1.0))
        .padding(Sides::length(24.0))
        .gap(10.0)
        .flex_wrap(FlexWrap::Wrap)
        .background(theme.background)
    }
}