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-Driven Template Selection: With 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

  1. Add data-template-key="propertyName" to a component element or data-list element
  2. Define <template data-type="value"> children for each variant
  3. Optionally include an untyped <template> as the default fallback
  4. The framework reads the named property (state or computed) and renders the matching template

Lists: Heterogeneous Items

The most common use case is 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)
    }
})
Live Preview

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'
    }
})
Live Preview
Tip: 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-bind and 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:

BindingWorks in Variants?Notes
data-bindYesResolves from item (list) or component state (standalone)
data-actionYesList actions receive details.item with correct item context
data-modelYesTwo-way binding per variant
data-show / data-renderYesConditional display per variant
data-bind-classYesDynamic classes per variant
data-bind-styleYesDynamic styles per variant
data-bind-attrYesDynamic attributes per variant
data-listYesNested lists inside a variant
data-componentYesNested 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 TemplatesConfigurable 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

Prefix support: All attributes also support the data-wf- prefix:
<div data-wf-template-key="viewType">
    <template data-wf-type="card">...</template>
    <template data-wf-type="table">...</template>
</div>

Quick Reference

AttributePlaced OnPurpose
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