Slots

Slots let a parent inject HTML content into designated areas of a child component. The child defines placeholders with data-slot-container, and the parent fills them with data-slot.

Basic Slots

A component defines slot containers in its layout markup. The parent provides content for each named slot. During initialization, the framework moves each data-slot element into the matching data-slot-container:

<div data-component="status-card">
    <!-- Component layout with slot containers -->
    <div class="card">
        <div class="card-header"
             data-slot-container="header"></div>
        <div class="card-body"
             data-slot-container="body"></div>
    </div>

    <!-- Parent-provided slot content -->
    <div data-slot="header">
        <h5 class="mb-0">System Status</h5>
    </div>
    <div data-slot="body">
        <p>Server: <strong data-bind="serverStatus"></strong></p>
        <p>Uptime: <span data-bind="uptime"></span></p>
        <button class="btn btn-primary btn-sm"
                data-action="refreshStatus">
            Refresh
        </button>
        <span class="ms-2 small text-muted"
              data-bind="lastRefresh"></span>
    </div>
</div>
wildflower.component('status-card', {
    state: {
        serverStatus: 'Online',
        uptime: '14 days, 3 hours',
        lastRefresh: ''
    },

    refreshStatus() {
        // Simulate a status check
        const statuses = ['Online', 'Degraded', 'Online', 'Online']
        const uptimes = [
            '14 days, 3 hours',
            '2 days, 11 hours',
            '30 days, 7 hours',
            '7 days, 22 hours'
        ]
        const i = Math.floor(Math.random() * statuses.length)

        this.serverStatus = statuses[i]
        this.uptime = uptimes[i]
        this.lastRefresh =
            'Refreshed at ' + new Date().toLocaleTimeString()
    }
})
Live Preview

How Slots Work

During component initialization, the framework finds every data-slot-container element and looks for a matching data-slot with the same name. The slot content is moved into the container, replacing any default content.

Attribute Placed On Purpose
data-slot-container="name" Child layout markup Placeholder where parent content will be inserted
data-slot="name" Parent-provided content Content to inject into the matching container

Multiple Named Slots

Components can define as many named slots as needed. This is useful for layout components like cards, dialogs, or page shells. Slot content is fully reactive: data-bind, data-action, and other attributes work inside slots:

<div data-component="confirm-dialog">
    <!-- Dialog layout with three slot containers -->
    <div class="card">
        <div class="card-header"
             data-slot-container="header"></div>
        <div class="card-body"
             data-slot-container="body"></div>
        <div class="card-footer d-flex justify-content-end gap-2"
             data-slot-container="actions"></div>
    </div>

    <!-- Slot content injected by the parent -->
    <div data-slot="header">
        <h5 class="mb-0">Confirm Deletion</h5>
    </div>
    <div data-slot="body">
        <p>Delete <strong data-bind="itemName"></strong>?</p>
        <p class="text-muted small mb-0">This cannot be undone.</p>
    </div>
    <div data-slot="actions">
        <button class="btn btn-secondary btn-sm"
                data-action="cancel">Cancel</button>
        <button class="btn btn-danger btn-sm"
                data-action="confirmDelete">Delete</button>
    </div>

    <!-- Result display -->
    <div class="mt-3 p-2 text-center" data-show="result">
        <span class="badge bg-info" data-bind="result"></span>
    </div>
</div>
wildflower.component('confirm-dialog', {
    state: {
        itemName: 'Annual Report.pdf',
        result: ''
    },

    nextFile() {
        const files = [
            'Budget.xlsx', 'Presentation.pptx',
            'Photo.jpg', 'Notes.txt'
        ]
        this.itemName =
            files[Math.floor(Math.random() * files.length)]
    },

    cancel() {
        this.result = `Kept "${this.itemName}"`
        this.nextFile()
    },

    confirmDelete() {
        this.result = `Deleted "${this.itemName}"`
        this.nextFile()
    }
})
Live Preview

Slots vs Props vs Configurable Templates

Pattern Passes Use When
Slots HTML content Parent provides markup for layout areas (headers, footers, actions)
Props Data values Parent passes typed, validated data that the child renders its own way
Configurable Templates Templates + child data Parent defines how to render, child provides the data context (like Vue scoped slots)