Tessera: UI Framework Designed with My Style in Mind

Hello everyone,

I'd like to introduce a project I'm working on: Tessera , an immediate-mode GUI framework for Rust. The project is still in its early stages (WIP), but the core design has solidified enough that I would appreciate some early feedback from the community.

The source code is available here: shadow3aaa/tessera on github

To see the current state of the project, the best place to look is the example crate.

The Core Philosophy: "GUI is not special"

The design principle behind Tessera is that UI code shouldn't require a special, complex object model that is separate from the rest of an application's logic. Instead, a UI component should simply be another function in your program.

This means developers don't need to implement struct Component or manage complex lifecycle hooks. In Tessera, a component is just a function annotated with a macro.

A Quick Glance

Here’s a small example to give you a feel for the API. This component displays a click counter from shared application data.

#[tessera]
fn value_display(app_data: Arc<AppData>) {
    // surface is a basic component that provides a colored, rounded rectangle surface
    surface(
        SurfaceArgsBuilder::default()
            .corner_radius(25.0)
            .color([0.9, 0.8, 0.8, 1.0]) // Light red fill, RGBA
            .build()
            .unwrap(),
        move || {
            // text is a child component that renders text
            text(
                app_data
                    .click_count
                    .load(atomic::Ordering::SeqCst)
                    .to_string(),
            );
        },
    )
}

Core Concepts

The framework is built around a few core concepts:

1. Function-Based Components:
All components are functions annotated with the #[tessera] macro. This macro handles the boilerplate of integrating the function into the component tree. Components can accept arguments and take other components as FnOnce() children.

2. Stateless by Default:
Components themselves don't hold state. State is managed externally (typically via Arc, atomics, or other shared state patterns) and passed into components as arguments. This helps keep the UI logic clean.

3. Explicit Layout and State Handling (When Needed):
For simple containers, the framework provides default layout behavior. For more complex components, you can provide closures to define custom logic. Here is a conceptual example showing the full structure of a component, which clearly separates layout, child component construction, and event handling:

use tessera_macros::tessera;

#[tessera]
pub fn my_component(
    // Parameters, like state, styling, children
    child: impl FnOnce(), // For components that accept children
) {
    // Optional: Define custom layout logic
    measure(Box::new(move |input| {
        // Calculate size, position children, set drawable
        // This runs during the measure pass
        Ok(ComputedData { width, height })
    }));

    // Required: Execute child components (if any)
    child(); // This builds the component tree

    // Optional: Handle user interactions and events
    state_handler(Box::new(move |input| {
        // Respond to clicks, keyboard, scroll events
        // This runs every frame after the measure pass
    }));
}

What I'm Looking For

I welcome feedback on any aspect of the project. I am particularly interested in your thoughts on the following:

  1. API Design & Ergonomics: Is the #[tessera] macro and the function-based component model intuitive? Are the measure and state_handler APIs clear?

  2. General Code Review: Any feedback on idiomatic Rust usage, architectural decisions (the project is a multi-crate workspace), potential performance issues, or anything else you notice would be very helpful.

Thank you for your time. I look forward to your thoughts and suggestions.

1 Like

Posting a GUI framework with no screenshots seems quite strange. I would really expect that. Maybe even a live demo if it works with wasm.

This specifically rubs me very much the wrong way, and you seem to apply it inconsistently across components. I would make surface itself return a builder, where either the initial or final function takes the closure as an argument. As it is, the ArgsBuilder pattern is unnecessarily verbose and clunky... but if that's how you like it, that's your choice to make.

Otherwise, it all seems good, but not all that unique. What would you say should make someone prefer this over, say, egui?

1 Like

For example a interactive_demo

I think the args pattern isn’t particularly verbose, especially since we can accept impl Into<Args> instead of requiring Args directly. For example, text(TextArgBuilder::default().text("hello").build().unwrap()) can simply be written as text("hello").

You mean something like

surface()
    .corner_radius(25.0)
    .color([0.9, 0.8, 0.8, 1.0])
    .child(move || { ... })
    .ui();

? That looks good but I like a simple function more

I don't see what's "simple" about a function that takes a builder for its arguments, especially when you have some arguments that aren't part of that builder. And having shortcuts via Into is nice, but also a bit confusing (you now need to know all the acceptable types), and also limited (as soon as you need to set even trivial options, you now have to revert to the full, cumbersome syntax).


And mobile support, but currently wasm has no support.

Egui is certainly a more mature and polished library in many respects. So you are right... but now there is a new feature that egui dont have: the rendering pipeline and render instructions have been made pluggable. This means the core library itself doesn't contain any shader code, as it's all handled and registered by the component library. Of course, I'm aware this feature alone isn't enough to make most people choose it over egui. I just want to say that I believe there's value in exploring new architectures.

1 Like

I'm not sure what you mean, egui has pluggable renderers too GitHub - emilk/egui: egui: an easy-to-use immediate mode GUI in Rust that runs on both web and native