Quick Start
The WildflowerJS model in under a minute. You define state, bind it to the DOM, and the framework updates the page when the state changes.
1. Plain State
Every component starts with a state object. This is the single source of truth for your UI:
wildflower.component('counter', {
state: {
count: 0
}
})
That's it. There are no classes or constructors, only data.
2. Bind to the DOM
Connect state to HTML with data-bind, and wire up interactions with data-action:
<div data-component="counter">
<p>Count: <span data-bind="count">0</span></p>
<button data-action="increment">+1</button>
</div>
wildflower.component('counter', {
state: {
count: 0
},
increment() {
this.count++
}
})
What Happens When You Click
- Click runs the method:
data-action="increment"callsincrement(), which setsthis.count++ - Proxy detects the mutation: WildflowerJS wraps state in a deep reactive Proxy. The assignment to
countis intercepted automatically. - Only that text node updates: the framework knows which DOM node is bound to
countand patches it directly, with no diffing and no virtual DOM.
this.count++, and the framework turns each one into a targeted DOM update.
3. Computed Values
Derive new values from state. Computed properties track their dependencies automatically and recalculate only when those dependencies change:
<div data-component="counter">
<p>Count: <span data-bind="count">0</span></p>
<p>Doubled: <span data-bind="count * 2">0</span></p>
<p>Is even: <span data-bind="isEven"></span></p>
<button data-action="increment">+1</button>
</div>
wildflower.component('counter', {
state: {
count: 0
},
computed: {
isEven() {
return this.count % 2 === 0 ? 'Yes' : 'No'
}
},
increment() {
this.count++
}
})
Two ways to derive values:
- Inline expressions:
data-bind="count * 2"for simple math or logic, evaluated directly in the binding - Computed properties:
isEven()for anything more complex. Results are cached until dependencies change.
4. Lists
Render arrays with data-list. Each item in the array gets its own copy of the <template>:
<div data-component="grocery-list">
<ul data-list="items">
<template>
<li data-bind="name"></li>
</template>
</ul>
<button data-action="addItem">Add Milk</button>
</div>
wildflower.component('grocery-list', {
state: {
items: [
{ name: 'Eggs' },
{ name: 'Bread' }
]
},
addItem() {
this.items.push({ name: 'Milk' })
}
})
Inside the template, data-bind="name" refers to each item's name property. Array mutations like push, splice, and filter are all reactive.
data-list must have a <template> child. The template defines the markup for each item.
5. Conditionals
Show or hide elements based on state with data-show:
<div data-component="grocery-list">
<p data-show="items.length > 0">
You have <span data-bind="items.length"></span> items.
</p>
<p data-show="items.length === 0">Your list is empty.</p>
<ul data-list="items">
<template>
<li data-bind="name"></li>
</template>
</ul>
</div>
data-show accepts any expression that evaluates to truthy or falsy. The element stays in the DOM but is hidden with display: none when the condition is false. For full add/remove from the DOM, use data-render instead.
6. Batching
Multiple state mutations in a single method produce one DOM update, not three:
wildflower.component('profile', {
state: {
firstName: '',
lastName: '',
status: 'idle'
},
loadUser() {
this.firstName = 'Jane' // mutation 1
this.lastName = 'Doe' // mutation 2
this.status = 'loaded' // mutation 3
// --> one DOM update at the end
}
})
WildflowerJS batches synchronous mutations. All three assignments are collected and flushed as a single render pass, so the intermediate states never reach the screen.
Putting It Together
Here is a working example that uses every concept from this page. Try it live:
<div data-component="task-tracker-demo">
<h5>Tasks</h5>
<p>Total: <span data-bind="tasks.length"></span>
| Done: <span data-bind="doneCount"></span></p>
<div class="d-flex gap-2 mb-2">
<input type="text" class="form-control form-control-sm"
data-model="newTitle" placeholder="New task...">
<button class="btn btn-primary btn-sm" data-action="addTask">Add</button>
</div>
<p data-show="tasks.length === 0">
<em>No tasks yet.</em>
</p>
<ul data-list="tasks" class="list-unstyled">
<template>
<li class="mb-1">
<input type="checkbox" data-model="done">
<span data-bind="title" class="ms-1"
data-bind-class="done ? 'text-muted text-decoration-line-through' : ''"></span>
</li>
</template>
</ul>
</div>
wildflower.component('task-tracker-demo', {
state: {
tasks: [
{ title: 'Learn WildflowerJS', done: false },
{ title: 'Build something', done: false }
],
newTitle: ''
},
computed: {
doneCount() {
return this.tasks.filter(t => t.done).length
}
},
addTask() {
const title = this.newTitle.trim()
if (title) {
this.tasks.push({ title, done: false })
this.newTitle = ''
}
}
})
Reactive state updates with computed properties, two-way model binding, and a configurable step size.
Next Steps
You now know the core model. State drives the DOM through bindings, and the Proxy intercepts each assignment so the framework can update the elements bound to it. From here:
- Components: Nested components, slots, and communication patterns
- State: Nested objects, arrays, and state organization
- Stores: Shared state across components
- Forms: Two-way binding with
data-model