Eleva.js Cheat Sheet
Setup
import Eleva from "eleva";
const app = new Eleva("MyApp");
app.component("Name", { setup, template, style, children });
app.mount(document.getElementById("app"), "Name", { props });
Signals (Reactive State)
setup: ({ signal }) => {
const count = signal(0); // Create signal
count.value; // Read value
count.value = 5; // Set value (triggers re-render)
count.value++; // Update value
// Objects/Arrays - REPLACE, don't mutate!
const items = signal([1, 2, 3]);
items.value = [...items.value, 4]; // ✓ Correct
items.value.push(4); // ✗ Won't trigger re-render
return { count, items };
}
| Operation |
Wrong (mutate) |
Right (replace) |
| Add item |
arr.push(x) |
arr.value = [...arr.value, x] |
| Remove item |
arr.splice(i,1) |
arr.value = arr.value.filter(...) |
| Update item |
arr[i] = x |
arr.value = arr.value.map(...) |
| Update object |
obj.key = x |
obj.value = {...obj.value, key: x} |
Template Syntax
template: (ctx) => `
<!-- Interpolation: use ctx. -->
<p>${ctx.count.value}</p>
<p>${ctx.items.value.length} items</p>
<!-- Events: NO ctx, use function name -->
<button @click="increment">+</button>
<button @click="() => setCount(5)">Set 5</button>
<input @input="(e) => setName(e.target.value)" />
<!-- Child Props: NO ctx -->
<child :data="items.value" :label="name.value"></child>
<!-- Conditionals -->
${ctx.show.value ? `<div>Visible</div>` : ''}
${ctx.error.value && `<span class="error">${ctx.error.value}</span>`}
<!-- Lists (always use key) -->
${ctx.items.value.map(item => `
<li key="${item.id}">${item.name}</li>
`).join('')}
`
Quick Rule
| Context | Use ctx.? | Example |
|———|————-|———|
| ${} interpolation | Yes | ${ctx.count.value} |
| @event handlers | No | @click="increment" |
| :prop binding | No | :data="items.value" |
Events
// Simple handler
<button @click="handleClick">Click</button>
// With argument
<button @click="() => remove(item.id)">Delete</button>
// With event object
<input @input="(e) => setQuery(e.target.value)" />
<form @submit="(e) => { e.preventDefault(); save(); }">
// Common events
@click @dblclick @mouseenter @mouseleave
@input @change @focus @blur
@keydown @keyup @keypress
@submit @reset
Lifecycle Hooks
setup: ({ signal }) => {
const data = signal(null);
let cleanup = null;
return {
data,
onBeforeMount: () => { /* Before DOM insert */ },
onMount: () => {
// DOM ready - fetch data, add listeners
fetchData().then(d => data.value = d);
cleanup = subscribe();
},
onUpdate: () => { /* After re-render (don't set state here!) */ },
onUnmount: () => {
// Cleanup - remove listeners, cancel requests
if (cleanup) cleanup();
}
};
}
| Hook |
When |
Use For |
onBeforeMount |
Before DOM insert |
Validate props |
onMount |
After DOM ready |
Fetch data, add listeners, focus |
onUpdate |
After re-render |
Sync external systems |
onUnmount |
Before removal |
Cleanup listeners, timers |
Component Structure
app.component("MyComponent", {
// 1. Setup (optional) - state & logic
setup: ({ signal, props, emitter }) => {
const count = signal(props.initial || 0);
const increment = () => count.value++;
return { count, increment, onMount: () => {} };
},
// 2. Template (required) - HTML structure
template: (ctx) => `
<div class="my-component">
<span>${ctx.count.value}</span>
<button @click="increment">+</button>
</div>
`,
// 3. Style (optional) - scoped CSS
style: `
.my-component { padding: 1rem; }
button { cursor: pointer; }
`,
// 4. Children (optional) - nested components
children: {
".child-slot": "ChildComponent"
}
});
Props & Events (Parent-Child)
// Parent
app.component("Parent", {
setup: ({ signal }) => {
const items = signal(["a", "b"]);
const handleAdd = (item) => items.value = [...items.value, item];
return { items, handleAdd };
},
template: (ctx) => `
<child-list :items="items.value" :onAdd="handleAdd"></child-list>
`,
children: { "child-list": "ChildList" }
});
// Child - receives props
app.component("ChildList", {
setup: ({ props }) => ({
items: props.items,
add: () => props.onAdd("new")
}),
template: (ctx) => `
<ul>${ctx.items.map(i => `<li>${i}</li>`).join('')}</ul>
<button @click="add">Add</button>
`
});
Emitter (Cross-Component)
setup: ({ emitter }) => {
// Emit event
const save = () => emitter.emit("data:saved", { id: 1 });
// Listen to event (remember to cleanup!)
let unsub;
return {
save,
onMount: () => {
unsub = emitter.on("data:saved", (data) => console.log(data));
},
onUnmount: () => unsub && unsub()
};
}
Plugins
import { Attr, Router, Store } from "eleva/plugins";
// Attr - accessibility & attributes
app.use(Attr);
// Store - global state
app.use(Store, {
state: { user: null, count: 0 },
actions: {
setUser: (state, user) => state.user.value = user,
increment: (state) => state.count.value++
}
});
// Router - SPA navigation
app.use(Router, {
mode: "hash", // or "history"
routes: [
{ path: "/", component: Home },
{ path: "/user/:id", component: User },
{ path: "*", component: NotFound }
]
});
Using Plugins in Components
setup: ({ store, router }) => ({
// Store
user: store.state.user,
login: (u) => store.dispatch("setUser", u),
// Router
currentPath: router.currentRoute.value.path,
goTo: (path) => router.navigate(path),
params: router.currentRoute.value.params
})
Common Patterns
Fetch Data on Mount
setup: ({ signal }) => {
const data = signal(null);
const loading = signal(true);
const error = signal(null);
return {
data, loading, error,
onMount: async () => {
try {
const res = await fetch("/api/data");
data.value = await res.json();
} catch (e) {
error.value = e.message;
} finally {
loading.value = false;
}
}
};
}
setup: ({ signal }) => {
const query = signal("");
let timer;
const search = (value) => {
query.value = value;
clearTimeout(timer);
timer = setTimeout(() => fetchResults(value), 300);
};
return { query, search, onUnmount: () => clearTimeout(timer) };
}
Toggle State
setup: ({ signal }) => {
const isOpen = signal(false);
const toggle = () => isOpen.value = !isOpen.value;
return { isOpen, toggle };
}
setup: ({ signal }) => {
const form = signal({ name: "", email: "" });
const updateField = (field, value) => {
form.value = { ...form.value, [field]: value };
};
const submit = (e) => {
e.preventDefault();
console.log(form.value);
};
return { form, updateField, submit };
}
Don’ts
// ✗ Don't mutate arrays/objects
items.value.push(x);
user.value.name = "new";
// ✗ Don't update state in onUpdate (infinite loop)
onUpdate: () => { count.value++; }
// ✗ Don't forget cleanup
onMount: () => { window.addEventListener("scroll", fn); }
// Missing onUnmount cleanup!
// ✗ Don't use ctx. in events
<button @click="ctx.increment"> // Wrong
<button @click="increment"> // Right
Quick Links