Coming From...

Already know React, Vue, Solid, Svelte, or Alpine? This guide maps concepts you already understand to WildflowerJS equivalents.

What changes: WildflowerJS uses standard HTML data-* attributes instead of custom template syntax. You mutate state in place rather than replacing it. There is no build step, virtual DOM, JSX, or framework-specific file format.

Quick Concept Map

No matter which framework you're coming from, here's how the core ideas translate:

Concept React Vue Svelte WildflowerJS
Component function + JSX .vue SFC .svelte file data-component + JS object
State useState() ref() / reactive() $state() state: {}
Computed useMemo() computed() $derived() computed: {}
Text output {count} {{ count }} {count} data-bind="count"
Event onClick={fn} @click="fn" onclick={fn} data-action="fn"
Conditional {cond && ...} v-if / v-show {#if} data-show / data-render
List .map() v-for {#each} data-list
Two-way bind Manual onChange v-model bind:value data-model
Global state Context / Redux Pinia Svelte stores wildflower.store()
Server data TanStack Query / SWR / RTK Query TanStack Query / useFetch TanStack Query / load functions data-query (built in)
Lifecycle useEffect() onMounted() onMount() init() / destroy()
Build step Required Recommended Required None

The endpoint, how fresh the data should stay, and the loading and error states are all declared on the query itself. There is no equivalent of TanStack Query or SWR to install. See Data queries.

Coming from React

WildflowerJS has no JSX and no hooks, and you mutate state in place instead of replacing it.

Component Definition

React
function Counter() {
    const [count, setCount] = useState(0);
    return (
        <button onClick={() => setCount(c => c + 1)}>
            Count: {count}
        </button>
    );
}
WildflowerJS
<div data-component="counter">
    <button data-action="increment">
        Count: <span data-bind="count">0</span>
    </button>
</div>
wildflower.component('counter', {
    state: { count: 0 },
    increment() { this.count++; }
});

State Updates

React requires immutable updates. WildflowerJS supports both patterns, but direct mutation is simpler and enables targeted DOM updates:

React
// Must create new references
setItems([...items, newItem]);
setItems(items.filter(i => i.id !== id));
setUser({ ...user, name: 'Alice' });
WildflowerJS
// Direct mutation: simpler and faster
this.items.push(newItem);
this.items.splice(index, 1);
this.user.name = 'Alice';

// Immutable patterns also work if you prefer
this.items = [...this.items, newItem];

Hooks → Component Properties

React
const [count, setCount] = useState(0);
const doubled = useMemo(() => count * 2, [count]);
useEffect(() => {
    console.log('mounted');
    return () => console.log('cleanup');
}, []);
WildflowerJS
wildflower.component('example', {
    state: { count: 0 },
    computed: {
        doubled() { return this.count * 2; }
    },
    init() { console.log('mounted'); },
    destroy() { console.log('cleanup'); }
});

Context / Redux → Stores

React (Context)
const CartContext = createContext();

function CartProvider({ children }) {
    const [items, setItems] = useState([]);
    return (
        <CartContext.Provider value={{ items, setItems }}>
            {children}
        </CartContext.Provider>
    );
}

// In child:
const { items } = useContext(CartContext);
WildflowerJS
wildflower.store('cart', {
    state: { items: [] },
    addItem(item) { this.items.push(item); }
});

// In any component:
wildflower.component('checkout', {
    subscribe: { cart: ['items'] },
    computed: {
        total() {
            return this.stores.cart.items
                .reduce((s, i) => s + i.price, 0);
        }
    }
});
Differences from React: WildflowerJS has no JSX, build step, or hooks, so there are no rules of hooks, dependency arrays, stale closures, or memo/useCallback re-render optimization to manage. State is a plain object and methods are plain functions.

Deep Dive: Migrating from React →. Mental model shift, side-by-side Todo app, pattern-by-pattern migration, gotchas, and checklist.

Coming from Vue

Vue and WildflowerJS both use reactive state, direct mutation, and declarative templates. They differ in syntax. Vue uses custom directives like v-if and @click, and WildflowerJS uses standard data-* attributes.

Directive Mapping

VueWildflowerJSNotes
{{ count }}data-bind="count"Text interpolation
v-html="content"data-bind-html="content"Raw HTML
@click="method"data-action="method"Click is default event
@input="method"data-action="input:method"Prefix with event name
v-model="prop"data-model="prop"Two-way binding
v-if="cond"data-render="cond"DOM insertion/removal
v-show="cond"data-show="cond"CSS display toggle
v-for="item in items"data-list="items"Uses <template> child
:key="item.id"data-key="id"On the list container
:class="expr"data-bind-class="expr"Dynamic classes
:style="expr"data-bind-style="expr"Dynamic styles

Composition API → Component Definition

Vue 3 (script setup)
import { ref, computed } from 'vue'

const count = ref(0)
const doubled = computed(() => count.value * 2)
function increment() { count.value++ }
WildflowerJS
wildflower.component('counter', {
    state: { count: 0 },
    computed: {
        doubled() { return this.count * 2; }
    },
    increment() { this.count++; }
});

Pinia → Stores

Vue (Pinia)
export const useCartStore = defineStore('cart', {
    state: () => ({ items: [] }),
    getters: {
        total: (state) =>
            state.items.reduce((s, i) => s + i.price, 0)
    },
    actions: {
        addItem(item) { this.items.push(item); }
    }
});
WildflowerJS
wildflower.store('cart', {
    state: { items: [] },
    computed: {
        total() {
            return this.items.reduce((s, i) => s + i.price, 0);
        }
    },
    addItem(item) { this.items.push(item); }
});
Differences from Vue: There is no .value unwrapping, no build step, and no custom directive syntax, since data-* attributes are already valid HTML. Stores and plugins have the same structure as components (state, computed, methods), with no separate getters/actions blocks.

Deep Dive: Migrating from Vue →. Directive mapping, side-by-side Todo app, pattern-by-pattern migration, gotchas, and checklist.

Coming from Solid

Solid and WildflowerJS both avoid the virtual DOM and use fine-grained reactivity. Solid reads a signal by calling it, as count(). WildflowerJS reads the property directly, as this.count.

Signals → State

Solid
// Signals: for primitive values
const [count, setCount] = createSignal(0);
// Read: count()  Write: setCount(count() + 1)

// Stores: for nested objects/arrays
const [items, setItems] = createStore([]);
// Mutate: setItems(produce(s => s.push(item)))
// Or: setItems(items.length, newItem)
WildflowerJS
state: { count: 0, items: [] }

// Read: this.count  (property access)
// Write: this.count++
// Write: this.items.push(item)

createMemo → Computed

Solid
const doubled = createMemo(() => count() * 2);
// Access: doubled()
WildflowerJS
computed: {
    doubled() { return this.count * 2; }
}
// Access: this.doubled (or data-bind="doubled")

JSX Control Flow → Data Attributes

Solid
<Show when={isVisible()}>
    <div>Visible</div>
</Show>
<For each={items()}>
    {item => <li>{item.name}</li>}
</For>
WildflowerJS
<div data-show="isVisible">Visible</div>

<ul data-list="items" data-key="id">
    <template>
        <li data-bind="name"></li>
    </template>
</ul>
Differences from Solid: You read state as this.count rather than calling a getter like count(), and you mutate arrays and objects in place instead of going through produce(). There is no JSX or build step. The reactivity is fine-grained in both, and only the authoring model differs.

Deep Dive: Migrating from Solid →. Signal-to-state mapping, side-by-side Todo app, pattern-by-pattern migration, gotchas, and checklist.

Coming from Svelte

Svelte and WildflowerJS both favor direct mutation and minimal boilerplate. Svelte compiles .svelte files ahead of time, and WildflowerJS runs plain HTML in the browser as written.

Reactivity

Svelte 5
let count = $state(0);
let doubled = $derived(count * 2);

function increment() { count++; }
WildflowerJS
wildflower.component('counter', {
    state: { count: 0 },
    computed: {
        doubled() { return this.count * 2; }
    },
    increment() { this.count++; }
});

Template Syntax

Svelte
<button onclick={increment}>{count}</button>
{#if isVisible}<div>Shown</div>{/if}
{#each items as item (item.id)}
    <li>{item.name}</li>
{/each}
<input bind:value={name} />
WildflowerJS
<button data-action="increment">
    <span data-bind="count"></span>
</button>
<div data-render="isVisible">Shown</div>
<ul data-list="items" data-key="id">
    <template><li data-bind="name"></li></template>
</ul>
<input data-model="name" />
Differences from Svelte: There is no compiler, .svelte file format, or $state()/$derived() rune to deal with. The markup is standard HTML, so it works in any editor, on any server, and from any CDN without a build pipeline.

Deep Dive: Migrating from Svelte →. Runes-to-state mapping, side-by-side Todo app, pattern-by-pattern migration, gotchas, and checklist.

Coming from Alpine.js

Alpine is the closest relative to WildflowerJS. Both are attribute-based, no-build-step frameworks. WildflowerJS adds proper component lifecycle, stores with subscriptions, computed caching, and keyed list reconciliation.

Attribute Mapping

AlpineWildflowerJSNotes
x-data="{ count: 0 }"data-component="name"State is defined in JS, not inline
x-text="count"data-bind="count"
x-html="content"data-bind-html="content"
@click="count++"data-action="increment"Logic lives in JS methods, not inline
x-model="name"data-model="name"
x-show="isOpen"data-show="isOpen"Nearly identical
x-if="isOpen"data-render="isOpen"DOM insertion/removal
x-for="item in items"data-list="items"Uses <template> child
:class="expr"data-bind-class="expr"
Alpine.store('name', {})wildflower.store('name', {})Very similar API

Inline Logic → Defined Methods

Alpine
<div x-data="{ count: 0, get doubled() {
    return this.count * 2 } }">
    <button @click="count++">
        <span x-text="doubled"></span>
    </button>
</div>
WildflowerJS
<div data-component="counter">
    <button data-action="increment">
        <span data-bind="doubled"></span>
    </button>
</div>
wildflower.component('counter', {
    state: { count: 0 },
    computed: {
        doubled() { return this.count * 2; }
    },
    increment() { this.count++; }
});
Differences from Alpine: WildflowerJS caches computed properties, where an Alpine getter re-runs on every access. It reconciles keyed lists, gives components a full lifecycle (init, destroy, watch), and cleans up store subscriptions when a component is destroyed. Logic lives in JavaScript methods rather than inline in attributes.

Deep Dive: Migrating from Alpine.js →. Attribute mapping, scaling beyond Alpine, side-by-side Todo app, gotchas, and checklist.

SPA E-commerce Store

Client-side routing, product catalog, shopping cart, checkout flow, and toast notifications.

Universal Patterns

A few WildflowerJS patterns that don't have direct equivalents in other frameworks:

Cross-Entity Access

Access any store or component directly in HTML with the $ prefix:

<!-- Read from a store in any component's template -->
<span data-bind="$user.name"></span>
<div data-show="$auth.isLoggedIn">Welcome!</div>
<div data-list="$cart.items" data-key="id">
    <template><span data-bind="name"></span></template>
</div>

Store Subscriptions

Declarative store wiring with automatic cleanup on component destroy:

wildflower.component('dashboard', {
    subscribe: {
        user: ['profile', 'preferences'],
        cart: ['items']
    },

    // Fires when any subscribed path changes
    onStoreUpdate(storeName, path, newValue, oldValue) {
        if (storeName === 'cart') this.refreshTotals();
    }
});

List Action Context

Action handlers inside lists receive full context, including the item, its index, and parent list info:

deleteCard(event, element, details) {
    const card = details.item;           // Current list item
    const index = details.index;         // Position in array
    const column = details.parent.item;  // Parent list item (nested lists)
    this.items.splice(index, 1);
}

Built-in Store Persistence

One-line localStorage persistence, no middleware or plugins needed:

wildflower.store('settings', {
    storageKey: 'app-settings',  // localStorage key
    autoSave: true,              // Persists on every state change
    state: { theme: 'light', fontSize: 14 }
});