Skip to main content
Define captures a component subtree as a named template. Use references it by name, optionally injecting scoped data. The template is defined once and can appear any number of times in the tree with different data.

Basic Usage

from prefab_ui import Define, Use, UIResponse
from prefab_ui.components import Card, CardHeader, CardTitle, CardDescription, Column

with Define("user-card") as user_card:
    with Card():
        with CardHeader():
            CardTitle("{{ name }}")
            CardDescription("{{ role }}")

with Column(gap=3) as view:
    Use("user-card", name="Alice", role="Engineer")
    Use("user-card", name="Bob", role="Designer")
    Use("user-card", name="Carol", role="PM")

UIResponse(view=view, defs=[user_card])
Three cards with different data, but the card structure is defined once. The kwargs passed to Use become interpolation values scoped to that instance of the template.

Define

Define uses the context manager like any container, but it does not attach itself to a parent — it lives outside the component tree. Pass it to UIResponse via the defs parameter.
from prefab_ui import Define
from prefab_ui.components import Badge, CardTitle, Row

with Define("status-row") as status_row:
    with Row(gap=2, css_class="items-center"):
        CardTitle("{{ label }}")
        Badge("{{ status }}", variant="{{ variant }}")
UIResponse(view=layout, defs=[status_row])
If a Define contains multiple children, they’re automatically wrapped in a Column.

Use

Use references a Define by name. Any kwargs that aren’t base component fields (css_class) become scoped interpolation values for the template.
from prefab_ui import Use

# Bare reference (data comes from surrounding scope)
Use("status-row")

# With scoped data
Use("status-row", label="Build", status="passing", variant="default")

With ForEach

Inside a ForEach, each item’s fields become the interpolation context automatically — so Use doesn’t need explicit overrides:
from prefab_ui import Define, Use, UIResponse
from prefab_ui.components import (
    Card, CardHeader, CardTitle, CardDescription,
    Column, ForEach, Heading,
)

with Define("project-card") as project_card:
    with Card():
        with CardHeader():
            CardTitle("{{ name }}")
            CardDescription("{{ description }}")

with Column(gap=4) as view:
    Heading("Featured")
    Use("project-card", name="Prefab", description="The agentic frontend framework")

    Heading("All Projects")
    with ForEach("projects"):
        Use("project-card")

UIResponse(
    view=view,
    defs=[project_card],
    data={"projects": [
        {"name": "Alpha", "description": "First project"},
        {"name": "Beta", "description": "Second project"},
    ]},
)

API Reference

Define Parameters

name
str
required
Template name, referenced by Use. Can be passed as a positional argument.

Use Parameters

name
str
required
The template name to reference (must match a Define name). Can be passed as a positional argument.
**kwargs
Any
Scoped interpolation values. Any kwarg that isn’t css_class becomes a scoped state override for the template.
css_class
str | None
default:"None"
Additional Tailwind CSS classes.

Protocol Reference

Define serializes to the template body in the defs envelope:
defs
{
  "defs": {
    "user-card": {
      "type": "Card",
      "children": [...]
    }
  }
}
Use without overrides serializes to a $ref node:
Use (bare)
{"$ref": "user-card"}
With overrides, Use wraps the ref in a State node:
Use (with overrides)
{
  "type": "State",
  "state": {"name": "Alice", "role": "Engineer"},
  "children": [{"$ref": "user-card"}]
}