Intro to Components

Components are the building blocks of WildflowerJS applications. They encapsulate state, behavior, and templates into reusable, self-contained units declared entirely in HTML.

Defining a Component

Components are defined using wildflower.component() and declared in HTML with data-component:

<div data-component="user-card">
    <h3 data-bind="displayName"></h3>
    <p data-bind="user.email"></p>
    <button class="btn btn-primary" data-action="updateUser">Update User</button>
</div>
wildflower.component('user-card', {
    state: {
        user: { name: 'John Doe', email: 'john@example.com' },
        isLoading: false
    },

    computed: {
        displayName() {
            return this.user.name || 'Anonymous'
        }
    },

    updateUser() {
        const names = ['Alice Smith', 'Bob Johnson', 'Carol Williams', 'David Brown']
        const emails = ['alice@example.com', 'bob@example.com', 'carol@example.com', 'david@example.com']
        const index = Math.floor(Math.random() * names.length)

        this.user = {
            name: names[index],
            email: emails[index]
        }
    },

    init() {
        console.log('User card initialized:', this.id)
    }
})
Live Preview

Replacing a Definition

Registration is first-write-wins. Registering a name that already exists with a different definition keeps the original and ignores the new one, and development builds name the collision (WF-215), since it is almost always an accident: two components sharing a name, or a hot reload without teardown. Registering the identical definition again is silent, so re-running a setup script is safe.

To replace a definition, unregister the name first:

wildflower.unregister('task-card');   // removes the component and/or store by that name
wildflower.component('task-card', { /* the new definition */ });

unregister(name) is the unified entry: it removes a component, a store, or both if the name exists in both registries, and returns true when something was removed. The use cases are dynamic apps that swap definitions at runtime, hot-reload setups, and tests that re-register between cases.

Component Definition

A component definition can include any of these properties:

Property Type Description
state Object Reactive data properties. See State Management
computed Object Derived values from state. See Computed Properties
props Object Parent-to-child data passing. See Props
watch Object React to specific state or store changes. See Communication
subscribe Object/Array Declare store dependencies. See Communication
init(), destroy(), … Functions Lifecycle hooks. See Lifecycle Hooks
beforeInit() Function Runs before bindings are processed. See Lifecycle Hooks
beforeUpdate() Function Runs before DOM updates. See Lifecycle Hooks
onError() Function Error handler for the component. See Error Boundaries
onPropsChange() Function Runs when received props change. See Props
Action methods Functions Event handlers referenced by data-action. See Event Handling

Component Instance Properties

Every component instance has access to these properties via this:

Property Type Description
this.id String Unique component identifier
this.element HTMLElement Component's root DOM element
this.state Object Reactive state container
this.props Object Read-only props passed from parent
this.computed Object Computed property values
this.stores Object Subscribed store instances (when using subscribe)
this.getStore() Function Fetch any store by name, including ones you never declared in subscribe: this.getStore('cart'). Defaults to 'app-store'. See Stores
this.store() Function Read or write store state by path. this.store('total') reads from the default app-store, this.store('cart', 'total') reads from a named store, and a trailing value writes instead of reading
this.external() Function Read or write another component's or store's state: this.external('cart', 'total'). A single argument returns that entity's context
this.subscribe() Function Programmatic state subscription
this.emit() Function Child-to-parent communication. See Communication
this.$el() Function jQuery-like DOM helper. See DOM Helpers
this.find() Function Query selector within component: this.find('.btn')
this.findAll() Function Query selector all within component
this.closest() Function Find closest ancestor matching selector
this.rebindActions() Function Rebind data-action handlers after you replace markup yourself. Already-bound elements are skipped, so calling it repeatedly is safe. See Third-Party Libraries
this.update() Function Batch state updates: this.update({ count: 1, name: 'x' })
this.pools Object Declared pool handles, as in this.pools.enemies.add({...}). See Entity Pools
this.getPool() Function Fetch any pool by name with this.getPool('enemies'), including markup-only or imperative pools
this.parent Object Parent component's context (or null)
this.listItem Object List item data when inside a data-list (or null)
this.getItemFromEvent() Function Given a DOM event, return the list row or pool entity that encloses its target as { element, index, id, item }, or null. Pool entities report no index, since removal reshuffles storage. See Pool API
this.isReady() Function Whether the entity's state has finished loading. See Advanced Stores
this.waitForReady() Function Promise that resolves once state is ready, for awaiting an async store in init()
this.saveToStorage() Function Write state to storage now. Does nothing unless storageKey is set. See Advanced Stores
this.loadFromStorage() Function Reload state from storage, discarding what is in memory. Also requires storageKey

Component Composition

Components nest naturally in HTML. Each manages its own state independently:

<div data-component="dashboard">
    <header data-component="app-header"></header>

    <main>
        <aside data-component="sidebar-nav"></aside>
        <section data-component="main-content"></section>
    </main>

    <footer data-component="app-footer"></footer>
</div>

Dynamic Component Creation

Components can be created programmatically and added to the DOM at runtime:

const element = document.createElement('div')
element.setAttribute('data-component', 'notification-toast')
document.getElementById('toasts').appendChild(element)

// Tell the framework to initialize the new component
wildflower.scan(element)