Advanced Configurable Templates CORE+
Template hierarchy resolution, conditional rendering, ancestor targeting, async registration, and SSR support for configurable component templates.
Template Hierarchy Lookup
When a component uses data-use-template, the framework searches up the component hierarchy for a matching data-item-template. The closest ancestor wins.
<!-- Grandparent defines a default template -->
<div data-component="app-shell">
<template data-item-template="listItem">
<div class="p-2 border-bottom">
<span data-bind="label"></span>
<small class="text-muted ms-1">(default)</small>
</div>
</template>
<div class="row">
<!-- Left panel: Uses grandparent's template -->
<div class="col-6">
<h5>Default Template</h5>
<div data-component="item-list-a">
<div data-list="items">
<template data-use-template="listItem"></template>
</div>
</div>
</div>
<!-- Right panel: Parent overrides the template -->
<div class="col-6">
<h5>Custom Template</h5>
<div data-component="custom-wrapper">
<!-- This template shadows the grandparent's -->
<template data-item-template="listItem">
<div class="p-2 bg-light border rounded mb-1">
<strong data-bind="label"></strong>
<span class="badge bg-success ms-2">custom</span>
</div>
</template>
<div data-component="item-list-b">
<div data-list="items">
<template data-use-template="listItem"></template>
</div>
</div>
</div>
</div>
</div>
</div>
// App shell just provides structure
wildflower.component('app-shell', {
state: {}
})
// Custom wrapper provides an override template
wildflower.component('custom-wrapper', {
state: {}
})
// Both lists have the same data
wildflower.component('item-list-a', {
state: {
items: [
{ label: 'Item One' },
{ label: 'Item Two' },
{ label: 'Item Three' }
]
}
})
wildflower.component('item-list-b', {
state: {
items: [
{ label: 'Item One' },
{ label: 'Item Two' },
{ label: 'Item Three' }
]
}
})
Conditional Templates with data-show
Combine data-with with data-show to conditionally display templates:
<div data-component="user-selector">
<template data-item-template="selectedUserCard">
<div class="card border-primary">
<div class="card-header bg-primary text-white">
Selected User
</div>
<div class="card-body">
<h5 data-bind="name"></h5>
<p class="text-muted mb-0" data-bind="email"></p>
</div>
</div>
</template>
<div data-component="user-picker">
<div class="mb-3">
<button class="btn btn-primary me-2" data-action="selectAlice">Alice</button>
<button class="btn btn-primary me-2" data-action="selectBob">Bob</button>
<button class="btn btn-secondary" data-action="clearSelection">Clear</button>
</div>
<!-- Only shown when selectedUser exists -->
<div data-use-template="selectedUserCard"
data-with="selectedUser"
data-show="selectedUser"></div>
<div class="text-muted" data-show="!selectedUser">
No user selected
</div>
</div>
</div>
wildflower.component('user-selector', {
state: {}
})
wildflower.component('user-picker', {
state: {
selectedUser: null
},
selectAlice() {
this.selectedUser = {
name: 'Alice Chen',
email: 'alice@example.com'
}
},
selectBob() {
this.selectedUser = {
name: 'Bob Smith',
email: 'bob@example.com'
}
},
clearSelection() {
this.selectedUser = null
}
})
Multiple Templates in One Component
A single component can use multiple parent-defined templates for different parts of its content:
<div data-component="article-layout">
<!-- Define multiple templates -->
<template data-item-template="articleHeader">
<div class="border-bottom pb-3 mb-3">
<h2 data-bind="title"></h2>
<div class="text-muted">
By <span data-bind="author"></span> |
<span data-bind="date"></span>
</div>
</div>
</template>
<template data-item-template="articleBody">
<div class="lead mb-4" data-bind="summary"></div>
<div data-bind="content"></div>
</template>
<template data-item-template="articleFooter">
<div class="border-top pt-3 mt-4 text-muted small">
Tags: <span data-bind="tags"></span> |
<span data-bind="readTime"></span> min read
</div>
</template>
<div data-component="article-content">
<div data-use-template="articleHeader" data-with="header"></div>
<div data-use-template="articleBody" data-with="body"></div>
<div data-use-template="articleFooter" data-with="footer"></div>
</div>
</div>
wildflower.component('article-layout', {
state: {}
})
wildflower.component('article-content', {
state: {
header: {
title: 'Getting Started with Configurable Templates',
author: 'Jane Developer',
date: 'January 7, 2026'
},
body: {
summary: 'Learn how to create flexible, reusable components.',
content: 'Configurable templates let parent components control presentation while child components manage data...'
},
footer: {
tags: 'javascript, components, patterns',
readTime: 5
}
}
})
Explicit Ancestor Targeting
When multiple ancestors define templates with the same name, the closest ancestor wins by default. If you need to skip closer ancestors and target a specific one, use the @componentName syntax:
<!-- Grandparent has a template -->
<div data-component="grandparent">
<template data-item-template="cardTemplate">
<div class="grandparent-style"><span data-bind="name"></span></div>
</template>
<!-- Parent also has a template with the same name -->
<div data-component="parent">
<template data-item-template="cardTemplate">
<div class="parent-style"><span data-bind="name"></span></div>
</template>
<div data-component="child">
<!-- For single object -->
<div data-use-template="cardTemplate@grandparent" data-with="item"></div>
<!-- For lists -->
<div data-list="items">
<template data-use-template="cardTemplate@grandparent"></template>
</div>
</div>
</div>
</div>
@ must match exactly the data-component attribute value of the target ancestor.
Async Template Registration
Templates are normally registered when a component initializes. For templates loaded dynamically after initialization, use rescanItemTemplates():
// After dynamically adding templates to the DOM
const parentElement = document.querySelector('#my-component')
const newTemplates = wildflower.rescanItemTemplates(parentElement)
console.log('Newly registered templates:', newTemplates)
// Output: ['asyncCard', 'asyncRow']
itemTemplateReady event fires once per new template found.
Template Ready Event
Components can listen for the itemTemplateReady event when templates become available:
// Listen for new templates on a component
document.querySelector('#my-component').addEventListener('itemTemplateReady', (e) => {
console.log('Template ready:', e.detail.templateName)
console.log('Component:', e.detail.component)
// Re-render lists that were waiting for this template
e.detail.component.state.items = [...e.detail.component.state.items]
})
SSR Support
When using configurable templates with server-side rendering, the framework automatically adds data-wf-used-template markers to rendered items. This enables proper hydration on the client:
<!-- Server-rendered output includes template markers -->
<div data-list="users">
<template data-use-template="userCard"></template>
<!-- Rendered items include which template was used -->
<div class="user-card" data-index="0" data-wf-used-template="userCard">...</div>
<div class="user-card" data-index="1" data-wf-used-template="userCard">...</div>
</div>
If a fallback template was used (parent template not found), the marker indicates this:
<!-- Marker shows fallback was used -->
<div class="default-item" data-wf-used-template="userCard:fallback">...</div>
Reference
| Attribute | Location | Description |
|---|---|---|
data-item-template="name" |
Parent component | Defines a named template that descendant components can use |
data-use-template="name" |
Child component | References an ancestor's template for rendering |
data-with="path" |
With data-use-template |
Binds template to a specific state path (for single objects outside lists) |
data-use-template="name@component" |
Child component | References a specific ancestor's template (skips closer ones) |
data-template-fallback="name" |
Sibling of data-use-template |
Fallback template if the named template isn't found |
data-wf-used-template="name" |
Rendered items | SSR marker indicating which template was used (auto-generated) |
JavaScript API
| Method | Description |
|---|---|
wildflower.rescanItemTemplates(element) |
Re-scan a component for newly added templates. Returns array of new template names. |
Events
| Event | Detail | Description |
|---|---|---|
itemTemplateReady |
{ templateName, component } |
Fired when a new template is registered via rescanItemTemplates() |
data-wf- prefix. For example:
<template data-wf-item-template="userCard">...</template>
<template data-wf-use-template="userCard"></template>
<div data-wf-use-template="userCard" data-wf-with="user"></div>