Skip to content

Layout Macro

wirelessEye edited this page Jun 30, 2026 · 2 revisions

Layout Macro

layout! is Nestix's component tree syntax. It expands to normal calls to create_element, generated prop builders, computed, and built-in list helpers.

Element Syntax

Create an element by naming a component type.

layout! {
    App
}

Add props with parentheses:

layout! {
    Button(.disabled = false)
}

Pass an existing props value with $ before the parentheses:

let props = build_props!(ButtonProps(.title = "Click".to_string()));

layout! {
    Button$(props)
}

Add children with braces:

layout! {
    Div {
        Text("Hello")
        Button { Text("Click") }
    }
}

If a component has children, layout! sets a .children prop automatically. The component's props type must include a compatible children field.

Expression Splices

Use $(expr) to splice an existing element or iterable of elements into a layout.

layout! {
    Fragment {
        $(props.children.clone())
    }
}

The expression is converted through ToElements when the surrounding layout contains multiple items.

Conditionals

Use normal if and else branches.

layout! {
    if count.get() % 2 == 0 {
        Text("Even")
    } else {
        Text("Odd")
    }
}

Conditionals are reactive when they read signals. The generated output uses a computed value so the branch updates when dependencies change.

Branches can contain one or many elements:

layout! {
    Div {
        if logged_in.get() {
            Header
            Dashboard
        } else {
            LoginForm
        }
    }
}

Keyed Lists

Use for item in data to render a signal-backed list. If no key is supplied, the item itself is used as the key.

layout! {
    for item in items.clone() {
        Text(item)
    }
}

For non-identity keys, add where key = ....

layout! {
    for item in items.clone() where key = |item| item.0.clone() {
        TodoListItem(.data = item)
    }
}

The generated child closure receives each item as a Readonly<Item>, so child props can react when an existing keyed item changes.

List data must be a signal whose output is cloneable and iterable. Keys must be stable, Eq, and Hash.

Yielded Items

Prefix an element or splice with yield when that item must be created inside the generated computed closure.

layout! {
    yield Text(computed!([label] || label.get()))
}

This is an advanced escape hatch for cases where pre-creating an element outside the computed output would capture stale values or produce the wrong identity.

Binding Host Handles

An element can bind its host handle into a state using name @ Component.

let input_handle = create_state(None);

layout! {
    input_handle @ Input(.value = "Hello".to_string())
}

The generated code creates an effect that writes element.handle() into the state. This is useful when renderer components expose host handles.

Child Closures

Some components accept a child factory rather than a static Layout. The layout parser supports closure arguments after an element's optional capture list.

layout! {
    SomeComponent [items] |item| {
        Text(item.get())
    }
}

The generated .children value is a callback! returning a PropValue of the nested layout.

Capture Lists

For child layout closures, bracketed captures clone values into the closure.

layout! {
    Menu [selected, label: props.label] |item| {
        MenuItem(.selected = selected.clone(), .label = label.clone())
    }
}

Capture syntax matches closure! and callback!:

  • [name] clones name;
  • [alias: expr] clones expr into a local named alias.

Output Shape

Depending on its contents, layout! may return:

  • ();
  • a single Element;
  • Vec<Element>;
  • a computed signal that produces an element or vector of elements.

Components usually return the macro expression directly and let the ComponentOutput machinery mount it.

Clone this wiki locally