Create a reusable component
Extract repeated markup into a .xmlui file with properties and slots.
A project dashboard often renders the same task-card layout in several places — a My Tasks panel, an All Tasks panel, a sprint board, and so on. Copy-pasting the block is a fast start, but the copies quickly drift: one gets a status icon, another gets a wider priority badge, and soon you're maintaining four versions of the same thing. Extract the block into a single TaskCard component and every panel automatically benefits from any future change.
<App>
<HStack wrapContent itemWidth="50%">
<VStack>
<H4>My Tasks</H4>
<TaskCard
title="Write release notes"
priority="high"
assignee="Alice"
dueDate="2026-04-10"
/>
<TaskCard
title="Review pull request"
priority="normal"
assignee="Alice"
dueDate="2026-04-12"
/>
</VStack>
<VStack>
<H4>All Tasks</H4>
<TaskCard
title="Write release notes"
priority="high"
assignee="Alice"
dueDate="2026-04-10"
/>
<TaskCard
title="Update dependencies"
priority="low"
assignee="Bob"
dueDate="2026-04-15"
/>
<TaskCard
title="Fix login bug"
priority="high"
assignee="Carol"
dueDate="2026-04-08"
/>
</VStack>
</HStack>
</App><Component name="TaskCard">
<Card>
<VStack>
<Text variant="strong">{ $props.title ?? '(untitled)' }</Text>
<HStack>
<Badge
value="{ $props.priority ?? 'normal' }"
colorMap="{{
high: '$color-warn',
normal: '$color-success'
}}"
/>
<Text variant="secondary" fontSize="$fontSize-sm">{ $props.assignee }</Text>
</HStack>
<Text variant="secondary" fontSize="$fontSize-sm">Due: { $props.dueDate }</Text>
</VStack>
</Card>
</Component><App>
<HStack wrapContent itemWidth="50%">
<VStack>
<H4>My Tasks</H4>
<TaskCard
title="Write release notes"
priority="high"
assignee="Alice"
dueDate="2026-04-10"
/>
<TaskCard
title="Review pull request"
priority="normal"
assignee="Alice"
dueDate="2026-04-12"
/>
</VStack>
<VStack>
<H4>All Tasks</H4>
<TaskCard
title="Write release notes"
priority="high"
assignee="Alice"
dueDate="2026-04-10"
/>
<TaskCard
title="Update dependencies"
priority="low"
assignee="Bob"
dueDate="2026-04-15"
/>
<TaskCard
title="Fix login bug"
priority="high"
assignee="Carol"
dueDate="2026-04-08"
/>
</VStack>
</HStack>
</App><Component name="TaskCard">
<Card>
<VStack>
<Text variant="strong">{ $props.title ?? '(untitled)' }</Text>
<HStack>
<Badge
value="{ $props.priority ?? 'normal' }"
colorMap="{{
high: '$color-warn',
normal: '$color-success'
}}"
/>
<Text variant="secondary" fontSize="$fontSize-sm">{ $props.assignee }</Text>
</HStack>
<Text variant="secondary" fontSize="$fontSize-sm">Due: { $props.dueDate }</Text>
</VStack>
</Card>
</Component>Key points
File placement and naming: The file is components/TaskCard.xmlui. The name attribute on <Component> must match the filename exactly, and the name must start with an uppercase letter. XMLUI discovers files in the components/ folder automatically.
Accessing props with $props: Inside a user-defined component, every caller-supplied attribute is available through $props. There is no other way to read them:
<Component name="TaskCard">
<Text>{ $props.title }</Text> <!-- ✅ correct -->
<Text>{ title }</Text> <!-- ❌ title is not in scope -->
</Component>Guard against missing props with ??: A caller may omit an optional attribute. The nullish-coalescing operator ?? provides a safe fallback without suppressing intentional false or 0 values:
{ $props.title ?? '(untitled)' } <!-- falls back when title is absent -->
{ $props.priority ?? 'normal' } <!-- falls back when priority is absent -->Scope isolation, with globals as an escape hatch: Local variables (declared with var.) and element IDs in the parent are invisible inside TaskCard by default. There are two ways to get values across that boundary:
Pass via props for component-specific data — the card receives only what it needs and is reusable in different contexts:
<App var.currentUser="Alice">
<TaskCard assignee="{currentUser}" title="Fix bug" />
</App>
<!-- inside TaskCard: $props.assignee works, currentUser does not -->Use a global for app-wide values that many components share — current user, theme mode, feature flags, derived utilities. Globals are declared with global. on the root of Main.xmlui, or as top-level declarations in a Globals.xs code-behind file. They are visible from every component without prop-threading:
<App global.currentUser="Alice">
<TaskCard title="Fix bug" />
</App>
<!-- inside TaskCard: { currentUser } reads the global directly — no $props needed -->Prefer props when the same component is rendered with different data in different places. Prefer globals when every consumer reads the same singleton value and prop-threading would only create boilerplate. See Scoping › Global variables and Scripting › Code-Behind files for details on globals and Globals.xs.
id is reserved — never use it as a prop name: The id attribute is special in XMLUI: it registers a named reference that parent built-in components can use to call the component's API. If you pass id="{$item.id}" to a user-defined component, XMLUI captures it as the component reference, not as $props.id. Use a descriptive name instead:
<!-- ❌ wrong: id is captured by the framework, $props.id is undefined -->
<TaskCard id="{task.id}" />
<!-- ✅ correct: use a dedicated prop name -->
<TaskCard taskId="{task.id}" />Why extract: Once TaskCard.xmlui exists, adding a status icon, changing the priority color, or tweaking padding is a one-file change that takes effect everywhere the component is used.
See also
- User-defined components — fuller treatment of the reusable-component story, including slots and template properties
- Scoping — how variables and IDs flow across component boundaries, with rules for globals
- Scripting › Code-Behind files — pairing a
.xmlui.xsfile with the component for shared helper functions