Templates
A template is a deferred block of components. The components inside a template are not created immediately — they are created each time the template is invoked.
Template Type
The Template type represents a template. Parameters are declared by listing their types inside the parentheses.
Template() // no parameters
Template(String) // one String parameter
Template(String, Int) // two parameters
Accepting Templates as Properties
A component accepts a template by declaring a @property of type Template(…).
import { VStack, Text } from ui
component Panel {
@property title: String
@property children: Template()
VStack {
Text(self.title)
self.children()
}
}
When a property is named children with type Template(), callers may use a trailing block:
var panel = Panel(title: "My Panel") {
Text("First item")
Text("Second item")
}
Parameterized Templates
Templates can receive parameters, allowing the caller to use the passed values when declaring the children.
import { VStack, Text } from ui
component List {
@property data: [String]
@property item: Template(String)
VStack {
for text in self.data {
self.item(text)
}
}
}
export var main: Component = List(
data: ["Alpha", "Beta", "Gamma"],
item: template (text) {
Text(text)
}
)
Declarative Loops and Conditionals
Inside a template body, for and if work declaratively — see Declarative Control Flow for full details.
Templates vs Functions
Both templates and functions can be passed as properties to components, but they have fundamentally different behaviour in reactive contexts.
When a function is called in a reactive expression — such as a component property or computed state — the reactive system records a dependency on every argument passed to it. Whenever any of those arguments change, the expression re-evaluates and the function is called again. If the function creates components, this means new component instances are created on every re-evaluation, which is expensive and leads to components being needlessly destroyed and recreated.
// Problematic: item is a function that creates a component.
// Calling item(text) makes the surrounding context depend on "text".
// Every time "text" changes, a new component is created.
component BadList {
@property data: [String]
@property item: (String) -> (Component)
VStack {
for text in self.data {
self.item(text) // new component instance on every change
}
}
}
Templates, by contrast, lazily forward their parameters. Calling a template does not make the reactive context depend on its arguments — the template itself is responsible for tracking what it needs internally. This means the same component instances are reused across re-evaluations, only updating the parts that actually changed.
// Correct: item is a Template. Calling self.item(text) does not
// create a dependency on "text" in the surrounding context.
component GoodList {
@property data: [String]
@property item: Template(String)
VStack {
for text in self.data {
self.item(text) // reuses existing component, updates internally
}
}
}
As a rule of thumb: use Template whenever the purpose is to produce components. Reserve function properties for callbacks and logic that does not create components.