Installation

WildflowerJS installs with one script tag. There are no build tools, compilation steps, or configuration files.

Zero Configuration Required:
  • 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-pool.min.js Core + Stores + data-pool (no data-list, plugins, portals, transitions, modals) Games, simulations, and per-frame visualization that render through pools
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-list + data-pools (no plugins, portals, transitions, modals) Minimal footprint needing both rendering primitives, lists and high-frequency entities
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 + Data queries SEO/SSR applications, and anything reading from a server

Development builds (.dev.js) include all console messages. Production builds (.min.js) strip debug logging but keep error messages.

One demo per bundle:

Nano Tip Splitter

A complete interactive widget in one HTML file. Computed totals, two-way inputs, reactive styling, and a conditional message.

Mini Todo List

Reactive lists with input binding, filtering, item actions, and computed counts. This is what data-list adds over Nano.

Lite Boids Flocking

800+ autonomous agents at native frame rate. Spatial grid neighbor search, predator/prey dynamics, and canvas trails. Every boid is a DOM element.

Core Kanban Board

Full-featured kanban with drag-and-drop, modals, animated transitions, search filtering, and localStorage persistence.

SPA E-commerce Store

Complete SPA with client-side routing, product catalog, shopping cart, checkout flow, and toast notifications.

Full Optimistic Tasks

Declarative writes against a slow server. Every change shows on screen first, temp ids are replaced on confirm, and an armable reject switch shows field-level rollback live.

What the Nano build includes

Nano is the smallest bundle, and it is still a complete reactive framework. Those ~51 KB (brotli) cover everything you need to ship a real interactive widget, rather than 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-show to toggle visibility, data-render to 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, and data-bind-attr, with attribute sanitization that blocks on* handlers and javascript: URLs
  • Directives and hooks for your own extensions
  • WildQuery, the built-in jQuery-style DOM helper

Why 48 KB and not 5 KB? Because that list is what a widget needs once it leaves a demo. 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 includes all of them and nothing above them.

Nano and Mini differ by one feature, list rendering (data-list). The tiers above Mini add entity pools, portals, transitions, modals, plugins, routing, and server-side rendering. Use Mini when you want a declarative collection that stays in sync with an array. Nano already includes everything below that point, 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 detects dynamic components 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.

Nano World Clock

Builds its add/remove city wall by cloning a plain <template>. No data-list.

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>

ES Modules

Every tier also ships as an ES module (from 1.5.1), named .esm.min.js beside the script-tag file. The default export is the framework instance, and the same wildflower global is registered, so a page behaves the same whichever file it loads. Use it from a <script type="module">, from a bundler, or through the package's import condition:

<script type="module">
  import wildflower from 'https://cdn.jsdelivr.net/npm/wildflowerjs@1/dist/wildflower.esm.min.js';
  wildflower.component('counter', { state: { count: 0 }, increment() { this.count++ } });
</script>
// With a bundler or an import map: the package resolves the ES module for `import`
import wildflower from 'wildflowerjs';        // core tier
import wildflower from 'wildflowerjs/full';   // any tier: nano, mini-pool, mini, lite, spa, full

The named exports match the script-tag globals: wildflower, WildflowerJS, and on the SPA and Full tiers RouteManager (Full adds SSRManager). A .esm.dev.js twin carries the development diagnostics.

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.

Note: The framework creates a global 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" matches wildflower.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>

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.

The alternative: defer plus data-cloak. A blocking script costs you parse time, so Performance Optimization recommends <script defer> in <head> instead, which downloads in parallel and does not block rendering. The trade is that a deferred script runs only after parsing finishes, so your component HTML renders before the framework can populate it. Do not add defer to the example above: it removes the very thing that prevents the flash. Take the other route whole instead. Mark the affected elements data-cloak and add [data-cloak] { display: none !important; } to your stylesheet, which hides them until initialization removes the attribute. The !important matters, since [data-cloak] has the same specificity as a single class and any component rule setting display would otherwise win. One more condition on that route: defer belongs on external scripts only. If your component definitions are inline in the page, keep the framework tag blocking, as in the example above, because browsers ignore defer on inline scripts and the registrations would run before the framework exists.