Installation
Get started with WildflowerJS in minutes, not hours. No build tools, no compilation, no complex setup.
- No build tools or compilation
- No package.json complexity
- Works directly in browsers
- Standards-compliant HTML
Distribution Bundles
WildflowerJS provides pre-built bundles for different use cases:
| Bundle | Includes | Use Case |
|---|---|---|
wildflower.nano.min.js |
Core + Stores (no data-list, data-pools, plugins, portals, transitions, modals) | Smallest footprint; interactive widgets and single-file artifacts (data-bind, data-show, data-render, data-model, forms, computed, external) |
wildflower.mini.min.js |
Core + Stores + data-list (no data-pools, plugins, portals, transitions, modals) | Small footprint with list rendering (standard CRUD UI, forms, dashboards) |
wildflower.lite.min.js |
Core + Stores + data-pools (no plugins, portals, transitions, modals) | Minimal footprint with high-frequency entity rendering |
wildflower.min.js |
Core + Stores + Plugins + Portals + Transitions + Modals | Most applications |
wildflower.spa.min.js |
Core + Stores + All Features + Router | Single-page applications |
wildflower.full.min.js |
Core + Stores + All Features + Router + SSR | SEO/SSR applications |
Development builds (.dev.js) include all console messages. Production builds (.min.js) strip debug logging but keep error messages.
What the Nano build includes
Nano is the smallest bundle on the ladder, and it is still a complete reactive framework. The ~47 KB (brotli) buys the whole toolkit you need to ship a real interactive widget, not a bare signals primitive you have to build the rest around:
- Components with a full lifecycle (
init,destroy, update hooks) - Reactive state and computed properties, backed by a real dependency graph
- Two-way binding (
data-model) for inputs and forms - Conditionals:
data-showto toggle visibility,data-renderto insert and remove elements - Form handling with validation
- Error boundaries, so one broken component fails in place instead of taking down the page
- Stores for shared state, plus
external()to read across them - Expression evaluation, including a CSP-safe mode for strict environments
data-bind-class,data-bind-style, anddata-bind-attr, with attribute sanitization that blockson*handlers andjavascript:URLs- Directives and hooks for your own extensions
- WildQuery, the built-in jQuery-style DOM helper
Why 47 KB and not 5 KB? Because those are the pieces that separate a demo framework from one you can ship. Form validation, error isolation, XSS-safe attribute writes, and a shared-state layer stop being optional the moment someone starts typing into your widget. Nano hands you all of them at the floor of the build ladder, with nothing bolted on that you did not ask for.
The one line that separates Nano from Mini is list rendering (data-list). Above that sit the higher-tier features: entity pools, portals, transitions, modals, plugins, routing, and server-side rendering. For a declarative collection that keeps itself in sync with an array, step up to Mini. Everything below that line already ships in Nano, and you can still render a collection by hand when you need one (below).
Rendering a collection without data-list
Keep the array in state, write one item's markup once in a standard HTML <template>, and clone it on add. Appending the element is enough to initialize it, and removing it is enough to tear it down: the same mutation observer that powers dynamic component detection handles both, so there is no scan() or destroy call to remember.
<div id="wall"></div>
<template id="row-template">
<div data-component="clock-row">
<span data-bind="label"></span>
<button data-action="remove">Remove</button>
</div>
</template>
// Add an item: clone the template, append it, done.
addRow(value) {
this.rows = this.rows.concat(value); // array in state
const el = document.getElementById('row-template')
.content.firstElementChild.cloneNode(true);
el.dataset.value = value;
document.getElementById('wall').appendChild(el); // initialized automatically
}
// Inside the clock-row component, tear down on click:
remove() { this.element.remove(); } // destroyed automatically; destroy() runs
This pattern works in every build. The World Clock demo uses it for its add/remove city wall.
Installation Options
Script Tag
Add one script tag to your HTML:
<!-- Production (minified, no debug logs) -->
<script src="path/to/wildflower.min.js"></script>
<!-- Development (with debug logs) -->
<script src="path/to/wildflower.dev.js"></script>
<!-- Or enable debug via script attribute -->
<script src="path/to/wildflower.min.js" data-debug="true"></script>
Script Tag Configuration
Configure WildflowerJS directly from the script tag without any JavaScript:
| Attribute | Values | Default | Description |
|---|---|---|---|
data-debug |
true, "" |
false |
Enable debug mode and verbose logging |
data-error-handling |
log, throw, silent |
log |
How framework errors are handled |
data-auto-init |
true, false |
true |
Automatically initialize on page load |
data-wf-prefix |
true, false |
false |
Only process data-wf-* attributes |
<!-- Enable debug mode -->
<script src="wildflower.min.js" data-debug="true"></script>
<!-- Throw errors instead of logging (useful in development) -->
<script src="wildflower.min.js" data-debug data-error-handling="throw"></script>
<!-- Manual initialization (disable auto-init) -->
<script src="wildflower.min.js" data-auto-init="false"></script>
CDN
<!-- jsDelivr CDN -->
<script src="https://cdn.jsdelivr.net/npm/wildflowerjs@1/dist/wildflower.min.js"></script>
<!-- Smallest tier, for a single-file interactive widget -->
<script src="https://cdn.jsdelivr.net/npm/wildflowerjs@1/dist/wildflower.nano.min.js"></script>
<!-- With SPA routing -->
<script src="https://cdn.jsdelivr.net/npm/wildflowerjs@1/dist/wildflower.spa.min.js"></script>
Your First Component
Create a simple interactive component in 2 steps:
Step 1: HTML Structure
<div data-component="counter">
<h2>Counter Example</h2>
<p>Count: <span data-bind="count">0</span></p>
<button data-action="increment">+</button>
<button data-action="decrement">-</button>
<button data-action="reset">Reset</button>
</div>
Step 2: Component Definition
// The global 'wildflower' instance is created automatically
wildflower.component('counter', {
state: {
count: 0
},
increment() {
this.count++
},
decrement() {
this.count--
},
reset() {
this.count = 0
}
})
That's it! WildflowerJS auto-initializes and your counter is immediately reactive.
wildflower instance automatically.
You only need to create your own instance if you disable auto-init with data-auto-init="false".
Troubleshooting Common Issues
Component Not Initializing
If your component isn't initializing, make sure:
- The component name in
data-component="name"matcheswildflower.component('name', ...) - Your component definition script runs after WildflowerJS loads
- For dynamically added components, call
wildflower.scan()
<!-- Framework loads and creates global 'wildflower' -->
<script src="wildflower.min.js"></script>
<!-- Then register your components -->
<script>
wildflower.component('my-component', {
state: { message: 'Hello' }
})
</script>
Enable Debug Mode
Enable debug mode during development for helpful console output:
<!-- Option 1: Script tag attribute (recommended) -->
<script src="wildflower.min.js" data-debug="true"></script>
<!-- Option 2: Use development build -->
<script src="wildflower.dev.js"></script>
This provides detailed logging for component initialization, state changes, and binding updates.
Flash of Empty Lists or Content
If you see lists or bound content briefly appear empty before populating, the issue is script placement. Browsers progressively render HTML as it loads, so if your component HTML appears before the framework scripts, it will briefly render unpopulated.
Solution: Place framework scripts at the start of <body>, before your component HTML:
<body>
<!-- 1. Framework scripts FIRST -->
<script src="wildflower.min.js"></script>
<!-- 2. Component HTML AFTER scripts -->
<div data-component="my-app">
<div data-list="items">
<template>...</template>
</div>
</div>
<!-- 3. Component definitions at the end -->
<script src="app.js"></script>
</body>
Why this works: Script tags are blocking - the browser waits for them to load and execute before continuing to parse the HTML. By placing framework scripts first, the framework is ready before the browser even sees your component HTML.