Blog GitHub

"Reconstructive UI"

A UI programming paradigm

2026.09.18

Imagine a small contact management application. Its window contains a toolbar, a list of contacts, and a footer showing the number of contacts. Each contact has a name and an email address. A setting controls whether email addresses are visible.

An ordinary procedure builds this UI procedurally, using primitives provided by the UI framework.

To build this interface, the application uses primitives provided by a UI framework: create a container, add a label, add a button, set some attributes.

Here is a simplified pseudocode example:

build contact window:
    open column:
        open toolbar:
            add button "Add contact"
            add checkbox "Show email addresses", settings.showEmail

        open column:
            for contact in contacts:
                open row:
                    add label contact.name
                    if settings.showEmail:
                        add label contact.email

        add label "{contacts.count} contacts"

We call this type of code "UI builder code".

The builder code traverses application data and uses the framework's primitives to establish what belongs in the view. The construction is done via regular code, so we can make use of ordinary programming techniques like loops, conditionals, and functions.

Running this code produces a UI structure, which might look like the following, for the case where we have two contacts with email addresses visible:

Column
    Toolbar
        Button: Add contact
        Checkbox: Show email addresses
    Column
        Row
            Label: Alice
            Label: alice@example.com
        Row
            Label: Bob
            Label: bob@example.com
    Label: 2 contacts

The structure records the elements, their contents and attributes, and their relationships. It gives the framework the information it needs to display the interface. The framework can resolve sizes and positions, prepare drawing commands, and produce pixels on the screen.

Conceptually, the pipeline looks like this:

Application data
    ↓
[UI builder code]
    ↓
UI structure
    ↓
[Layout code]
    ↓
Rendering Primitives
    ↓
[Rendering code]
    ↓
Pixels on the screen

The application produces a UI structure, the framework produces pixels on the screen.

Building the structure and rendering it are separate stages. UI builder code that adds a label to the UI structure does not draw that label on the screen.

When the UI builder code runs again with different data, it produces a potentially different UI structure, which produces different pixels on the screen.

Suppose the user adds a third contact and disables email display: the builder code will produce the updated toolbar structure, with the "show email addresses" checkbox off this time. The for loop now produces three rows instead of two, and each row has only the name, without the email. The footer will now say "3 contacts".

At no point during the build process was there any type of "data binding" or any sort of registration of dependency relationships between the UI elements and the data sources that influence it.

The boolean that controls whether emails are displayed is just a regular boolean. Nothing "tracks" this boolean to update the UI when it's modified.

This is an example of Reconstructive UI, which I will define thus:

Reconstructive UI is a paradigm of UI programming where application code rebuilds the entire UI structure on each UI update.

Rationale

So why would you want to write the UI code this way? One important reason is that it allows the application data to remain as just regular data.

If the framework tries to track dependencies between UI elements and data sources that influence them, then it can't just use any type of data as a source, it has to be special: a state variable, or a signal. The idea is when you update the state variable, the UI framework updates just the elements that depend on that state.

The problem with this paradigm is that it makes managing the state variables difficult when that state is not just a data primitive.

In React for example, you can't just add an item to the array or mutate one field on one item, and expect the UI to reflect those changes.

The array itself must be a state variable, and in order to signal to the UI that it needs to refresh, the variable must change, and since the variable is an array reference, it's not going to change when you mutate it in-place. You have to create a new array.

setContacts(contacts =>
    contacts.map((contact, index) =>
        index === 0
            ? { ...contact, Name: "John" }
            : contact
    )
);

The Reconstructive UI paradigm, by contrast, allows you to just say:

contacts[0].Name = "John"
UpdateUI()

Here I am assuming UpdateUI() is the name of the function you use to tell the framework that your data has changed and you want it to schedule a full UI update at the next frame.

In the React example, extracting a ContactEdit component becomes cumbersome: the component has to be written in such a way that not only it edits the contact object, but it also updates the state variable that holds the larger structure within which the contact object resides.

function ContactEdit({ contact, onChange }) {
    return (
        <div>
            <input
                value={contact.name}
                onChange={e =>
                    onChange({
                        ...contact,
                        name: e.target.value
                    })
                }
            />

            <input
                value={contact.email}
                onChange={e =>
                    onChange({
                        ...contact,
                        email: e.target.value
                    })
                }
            />
        </div>
    );
}

Here we assume onChange is a helper function that the caller must supply.

function Contacts() {
    const [contacts, setContacts] = useState<Contact[]>([]);

    function updateContact(updated: Contact) {
        setContacts(contacts =>
            contacts.map(contact =>
                contact.id === updated.id
                    ? updated
                    : contact
            )
        );
    }

    return contacts.map(contact => (
        <ContactEdit
            key={contact.id}
            contact={contact}
            onChange={updateContact}
        />
    ));
}

Here, because the contact sits inside an array, the caller of ContactEdit has to know that and create the appropriate updateContact function.

Whereas the Reconstructive UI paradigm allows such a function to look more like this:

func ContactEditView(contact *Contact) {
    Column(
        Input(&contact.Name),
        Input(&contact.Email),
    )
}

Just taking pointers to the object(s) it wants to modify.

Because the UI state is just plain data, the Input field can be pointed directly to the string field it's meant to edit, without concerning itself about the larger structure.

Similarly, the contact edit view can just take a pointer to the contact.

func Contacts(contacts []Contact) {
    OpenColumn()
    for i := range contacts {
        contact := &contacts[i]
        ContactEditView(contact)
    }
    CloseColumn()
}

Much simpler!

Performance

A question arises then: wouldn't rebuilding the entire UI every time be expensive? Wouldn't it cause the UI to be slow, take too much CPU time, feel janky, etc?

No. Not in principle.

The UI structure is just data: containers, attributes, etc. Creating a basic data structure in memory is generally very fast.

A system where the UI only receives partial updates often comes with higher costs in terms of bookkeeping: you're either tracking dependencies, or duplicating application data with widget states, or both.

The bookkeeping can cost more than simply building the structure again; doing fewer structural updates does not automatically mean doing less work.

Immediate Mode UI

A lot of what we described here can also be reasonably described as "immediate mode", and we could just call it that.

But, the problem is that the term is loaded. It's heavily associated with game engine programming, and many people assume it's about the graphics side: that you are issuing commands for drawing the UI immediately.

The most popular C++ library for immediate mode UI is Dear ImGui, and most of the time, when you place an element, say a label or a button, the size and position of that element is resolved on the spot. There might be some special cases, but in general, that's the basic model. Many other immediate mode libraries follow the same model: egui, nuklear, microui.

If I understand correctly, they do not "draw" immediately, because that's not how graphics APIs work. Rather, what they immediately do is emit low-level drawing commands and primitives that specify exactly what to draw and where. The actual drawing happens later when these commands are pushed to the graphics card.

Gio does something slightly different: it issues draw commands to a local coordinate system. Layout then is performed by the container element "capturing" these drawing commands into a "macro" that is later "called" after an affine transformation is applied to the coordinate system.

The common thread amongst all of these immediate mode libraries is that you really do draw immediately (in the sense of issuing drawing commands).

Even though technically that is not a requirement of immediate mode UI, the fact that almost all the major or famous libraries do that means the idea has taken root and the term is now "loaded".

And this is actually par for the course.

The term "immediate mode" comes from graphics programming. The key distinction between immediate mode and retained mode is whether you issue drawing commands immediately, or you derive these commands from a scene graph. In immediate mode, you issue drawing commands "immediately", as in, on the spot. In retained mode, a scene graph is retained in the application code, and you control the graphics output by manipulating the scene graph.

In the model we propose in this article, neither term fits exactly. We do not create the UI by issuing drawing commands immediately, rather, we first build a structure that represents the UI, then let the framework use that structure to do the actual drawing.

But, the structure is not really retained: the whole point is that we rebuild the structure every single time we want to update the UI.

In typical retained mode, the structure is retained, and you manipulate it: add a node here, remove a node there, change this attribute, etc.

Our model is different: you do not update the UI structure partially, and you do not retain it. Rather, you just describe the structure again.

Though, for this model to be implemented properly, it does need to maintain some amount of data. Specifically, if an element needs to interact with mouse input, for example, it can only do so using information about its size and position from the previous frame.

This means we need to "match" an element in the UI structure with the version of itself that was present in the previous frame.

Generally speaking, this is very much feasible, as most of the time, the UI is consistent frame to frame. I go into this in more detail in the article about Component identity and retained state