Advanced Configurable Templates All

Template hierarchy resolution, conditional rendering, ancestor targeting, async registration, and SSR support for configurable component templates.

This page covers advanced configurable template patterns. For the basics, including defining templates, reactive binding, actions, lists, and fallbacks, see Configurable 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' }
        ]
    }
})
Live Preview

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
    }
})
Live Preview

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
        }
    }
})
Live Preview

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>
Note: The component name after @ 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']
Rescan behavior: Only newly discovered templates are registered and returned. Templates that already exist are skipped (no duplicates). The 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()
Prefix support: All attributes also support the 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>
Lifecycle & Cleanup: Templates registered during component initialization are automatically cleaned up when the component is destroyed. No manual unregistration is required.