Coming From...
Already know React, Vue, Solid, Svelte, or Alpine? This guide maps concepts you already understand to WildflowerJS equivalents.
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);
}
}
});
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
| Vue | WildflowerJS | Notes |
|---|---|---|
{{ 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); }
});
.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>
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" />
.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
| Alpine | WildflowerJS | Notes |
|---|---|---|
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++; }
});
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.
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 }
});