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)
}
)
Applying Modifiers to a Template Invocation
A template invocation is an expression producing components, so it can take a with block like any component — self.children() with { … }, self.item(text) with { … }. See Applying Modifiers to Expressions.
Declarative Loops and Conditionals
Inside a template body, for and if work declaratively — see Declarative Control Flow for full details.
No Variable Declarations
A template body — and likewise the trailing block passed to a children property — contains component expressions only. Variables cannot be declared inside them.
import { VStack, Text } from ui
component Panel {
@property children: Template()
VStack {
self.children()
}
}
export var main: Component = Panel {
var label = "First item" // error — no variable declarations inside a trailing block
Text("First item")
}
Compute the value somewhere it is allowed and pass it in instead — as a template parameter, or as a property or state of a component:
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"],
item: template (text) {
Text("Item: \{text}")
}
)
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.
import { VStack } from ui
// 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.
import { VStack } from ui
// 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.