Dynamic Templates All
Render different HTML templates based on a data property. Ideal for heterogeneous lists and multi-view components. Compare to Vue's <component :is>.
data-template-key, you define multiple <template data-type="X"> variants inline, and a state or item property selects which one renders. Unlike Configurable Templates (which resolve templates by name from the component hierarchy), dynamic templates select from inline siblings based on data values.
How It Works
- Add
data-template-key="propertyName"to a component element ordata-listelement - Define
<template data-type="value">children for each variant - Optionally include an untyped
<template>as the default fallback - The framework reads the named property (state or computed) and renders the matching template
Lists: Heterogeneous Items
The most common use case: a single list where each item has a type property that determines its template.
<div data-component="feed">
<div data-list="notifications" data-key="id" data-template-key="type">
<template data-type="info">
<div class="alert alert-info">
<strong>ℹ️</strong> <span data-bind="message"></span>
</div>
</template>
<template data-type="warning">
<div class="alert alert-warning">
<strong>⚠️</strong> <span data-bind="message"></span>
</div>
</template>
<template data-type="error">
<div class="alert alert-danger">
<strong>❌</strong> <span data-bind="message"></span>
<button class="btn btn-sm btn-outline-danger ms-2"
data-action="retry">Retry</button>
</div>
</template>
</div>
</div>
wildflower.component('feed', {
state: {
notifications: [
{ id: 1, type: 'info', message: 'Deploy succeeded' },
{ id: 2, type: 'warning', message: 'Disk at 85%' },
{ id: 3, type: 'error', message: 'Build failed' }
]
},
retry(event, element, details) {
alert('Retrying: ' + details.item.message)
}
})
Each item's type property (named by data-template-key) selects which <template data-type="..."> to clone. All standard bindings (data-bind, data-action, data-model, data-show, data-bind-class, etc.) work independently in each variant.
Default Fallback Template
An untyped <template> (no data-type) serves as the fallback when no type matches:
<div data-list="items" data-key="id" data-template-key="kind">
<template data-type="special">
<div class="special"><span data-bind="label"></span> ⭐</div>
</template>
<!-- Fallback for any unrecognized kind -->
<template>
<div class="default"><span data-bind="label"></span></div>
</template>
</div>
If no template matches and no default exists, a dev-mode warning is logged and nothing renders for that item.
Standalone Components: View Switching
For standalone components, data-template-key goes on the component element itself. When the named property changes (state or computed), the template swaps automatically:
<div data-component="profile-view"
data-template-key="viewType">
<!-- These buttons are OUTSIDE templates: they persist across swaps -->
<div class="d-flex gap-2 mb-3">
<button data-action="switchToCard"
class="btn btn-sm btn-primary">Card</button>
<button data-action="switchToTable"
class="btn btn-sm btn-secondary">Table</button>
</div>
<template data-type="card">
<div class="card p-3">
<h4 data-bind="name"></h4>
<p data-bind="role"></p>
</div>
</template>
<template data-type="table">
<div>
<table class="table table-sm">
<tr><td>Name</td><td data-bind="name"></td></tr>
<tr><td>Role</td><td data-bind="role"></td></tr>
</table>
</div>
</template>
</div>
wildflower.component('profile-view', {
state: {
viewType: 'card',
name: 'Jane Cooper',
role: 'Product Manager'
},
switchToTable() {
this.viewType = 'table'
},
switchToCard() {
this.viewType = 'card'
}
})
data-template-key must be on the component element itself (the element with data-component). Non-template children (elements placed before or after the <template> tags) are preserved automatically when templates swap, with no need to duplicate persistent UI in every variant.
Persistent Content
When a standalone component swaps templates, only the template-generated content is replaced. Any elements that are not inside a <template> survive the swap:
- Non-template children before and after templates persist across swaps
data-bindand other bindings on persistent content remain reactive- Nested components outside templates keep their state and lifecycle intact
- Template content renders at the original template position in the DOM
<div data-component="dashboard" data-template-key="viewType">
<!-- Persistent header: survives all template swaps -->
<h2 data-bind="title"></h2>
<template data-type="overview">
<div class="overview-panel">...</div>
</template>
<template data-type="detail">
<div class="detail-panel">...</div>
</template>
<!-- Persistent nav: survives all template swaps -->
<nav>
<button data-action="showOverview">Overview</button>
<button data-action="showDetail">Detail</button>
</nav>
</div>
Standalone: Nested Components
Templates can contain nested data-component elements. When swapping templates:
- Nested components in the old template are automatically destroyed (their
destroy()hooks fire) - Nested components in the new template are automatically initialized (their
init()hooks fire)
<div data-component="dashboard" data-template-key="mode">
<template data-type="analytics">
<div data-component="chart-widget">
<h3 data-bind="title"></h3>
<div class="chart-container"></div>
</div>
</template>
<template data-type="settings">
<div data-component="settings-form">
<input data-model="email" type="email" placeholder="Email">
</div>
</template>
</div>
Combined with Keyed Lists
data-template-key works alongside data-key for efficient keyed reordering of heterogeneous lists:
<div data-list="blocks" data-key="id" data-template-key="blockType">
<template data-type="text">
<div class="block-text"><p data-bind="content"></p></div>
</template>
<template data-type="image">
<div class="block-image"><img data-bind-attr="{ src: url, alt: caption }"></div>
</template>
<template data-type="code">
<div class="block-code"><pre><code data-bind="source"></code></pre></div>
</template>
</div>
wildflower.component('editor', {
state: {
blocks: [
{ id: 1, blockType: 'text', content: 'Hello world' },
{ id: 2, blockType: 'code', source: 'console.log("hi")' },
{ id: 3, blockType: 'image', url: '/photo.jpg', caption: 'A photo' }
]
}
})
Bindings Inside Variants
Each template variant has fully independent binding resolution. All binding types are supported:
| Binding | Works in Variants? | Notes |
|---|---|---|
data-bind | Yes | Resolves from item (list) or component state (standalone) |
data-action | Yes | List actions receive details.item with correct item context |
data-model | Yes | Two-way binding per variant |
data-show / data-render | Yes | Conditional display per variant |
data-bind-class | Yes | Dynamic classes per variant |
data-bind-style | Yes | Dynamic styles per variant |
data-bind-attr | Yes | Dynamic attributes per variant |
data-list | Yes | Nested lists inside a variant |
data-component | Yes | Nested components inside a variant |
Edge Cases
Missing Type Value
If the template key property is null, undefined, or an empty string, the framework falls back to the default template. If no default exists, nothing renders and a dev-mode warning is logged.
All Items Same Type
A degenerate case: if every item has the same type, only one template variant is used. This works correctly but offers no advantage over a standard single-template list.
State Preservation (Standalone)
When a standalone component swaps templates, the component's state and computed properties are preserved. Only the DOM changes; the component instance stays alive.
Dynamic vs. Configurable Templates
| Dynamic Templates | Configurable Templates | |
|---|---|---|
| Attribute | data-template-key |
data-use-template |
| Selection by | Data value (item property or state) | Template name (from component hierarchy) |
| Templates defined | Inline as siblings | In parent/ancestor component |
| Best for | Heterogeneous lists, multi-view components | Reusable components, parent-controlled rendering |
| Analogy | Vue <component :is> |
Vue Scoped Slots / React Render Props |
Prefix Support
data-wf- prefix:
<div data-wf-template-key="viewType">
<template data-wf-type="card">...</template>
<template data-wf-type="table">...</template>
</div>
Quick Reference
| Attribute | Placed On | Purpose |
|---|---|---|
data-template-key="prop" |
data-component or data-list element |
Names the property used to select a template |
data-type="value" |
<template> children |
Matches when the property equals this value |
(no data-type) |
<template> child |
Default fallback when no type matches |