Core Docs Common questions, testing guide, and troubleshooting tips.
Q: What is Eleva?
Eleva is a minimalist, lightweight (~2.5KB gzipped) pure vanilla JavaScript frontend framework. It provides React-like component-based architecture with signal-based reactivity, but without the complexity, dependencies, or mandatory build tools of larger frameworks.
Q: What’s Eleva’s core philosophy?
💡 Vanilla JavaScript. Elevated.
Eleva takes plain vanilla JavaScript to the next level. Signals for reactivity. Components for structure. Your JS knowledge stays front and center, not hidden behind abstractions. If it works in vanilla JavaScript, it works in Eleva.
Q: Is Eleva production-ready?
Yes! Eleva v1.2.0 is the latest stable release. The framework is production-ready with a stable API and comprehensive test coverage. We continue to welcome feedback and contributions.
Q: How do I report issues or request features?
Please use the GitHub Issues page.
Q: What is the difference between Eleva and React?
Eleva differs from React in several key ways:
Choose Eleva when bundle size and performance matter without build complexity; choose React when you need its extensive ecosystem and tooling.
Q: What is the difference between Eleva and Vue?
Both Eleva and Vue are progressive frameworks, but Eleva is smaller (~2.5KB vs ~45KB gzipped), has zero dependencies, and requires no build tools. Vue offers a more comprehensive ecosystem with Vue Router, Vuex/Pinia, and extensive tooling. Eleva’s plugins (Router, Store) provide similar functionality in a lighter package.
Q: What is the difference between Eleva and Svelte?
Svelte compiles components at build time, resulting in very small runtime code (~2KB), but requires a build step. Eleva (~2.5KB gzipped) works without any build tools via CDN. Both avoid virtual DOM. Choose Eleva when avoiding build complexity; choose Svelte when you’re already using a bundler.
Q: What is the difference between Eleva and SolidJS?
Both Eleva and SolidJS use signal-based reactivity and skip the virtual DOM. SolidJS (~7KB gzipped) requires a build step and uses JSX, while Eleva (~2.5KB gzipped) works without any build tools via CDN and uses template strings. Choose Eleva for simplicity and no build step; choose SolidJS when you prefer JSX and are already using a bundler.
Q: What is the difference between Eleva and Preact?
Preact (~5KB gzipped) is a lightweight React alternative with the same API, using Virtual DOM. Eleva (~2.5KB gzipped) is even smaller, uses signal-based reactivity instead of Virtual DOM, and uses template strings instead of JSX. Both can work without a build step. Choose Eleva for smaller size and no Virtual DOM overhead; choose Preact when you want React API compatibility.
Q: Is Eleva a React alternative?
Yes, Eleva can serve as a lightweight React alternative for projects that don’t need React’s full ecosystem. Eleva offers similar component-based architecture and reactivity patterns but with a much smaller footprint.
Q: How does Eleva’s reactivity work?
Eleva uses a signal-based reactivity system similar to Solid.js. Signals are reactive containers that hold values. When a signal’s value changes, any component or watcher subscribed to that signal automatically updates.
Q: Does Eleva use Virtual DOM?
No. Eleva uses real DOM manipulation with an efficient diffing algorithm. Instead of maintaining a virtual DOM tree in memory, Eleva directly patches the real DOM. This reduces memory overhead and delivers 240fps-capable performance.
Q: Can I use Eleva with TypeScript?
Absolutely! Eleva includes built-in TypeScript declarations (.d.ts files). No additional @types packages are needed.
Q: Does Eleva require a build step?
No. Eleva can be used directly via CDN without any build tools, bundlers, or transpilers. However, you can also use Eleva with bundlers like Vite, Webpack, or Rollup if you prefer.
Q: Is Eleva suitable for large applications?
Eleva’s performance scales well—it handles 10K+ rows efficiently (via virtual scrolling), achieves 240fps rendering, and its Router/Store plugins support complex SPAs. The main consideration for large applications is ecosystem maturity: React, Vue, and Angular offer more extensive tooling, testing libraries, and community resources.
Q: Does Eleva include routing capabilities?
Yes! Eleva includes a powerful Router plugin that provides client-side routing with navigation guards, reactive state, and component resolution. Import it from eleva/plugins.
Q: What plugins are available with Eleva?
Eleva comes with four powerful built-in plugins:
Q: What is Agent Experience (AX)?
Agent Experience (AX) is the developer experience of building AI-agent-powered applications. Eleva’s built-in Agent plugin provides a first-class integration surface for LLMs and AI agents — including an action registry, command bus, audit logging, schema validation, and scoped permissions — all in ~3.5KB gzipped.
Q: Does Eleva support AI and LLM integration?
Yes! Eleva includes a built-in Agent plugin that provides AI/LLM integration out of the box. It features an action registry, a structured command bus, audit logging, schema validation, and scoped permissions per component. No other ~2.5KB framework offers built-in agent support.
Q: Can I create custom plugins for Eleva?
Yes! Eleva has a simple plugin API. Plugins are objects with an install(eleva, options) method. See the Plugin System guide for details.
Q: Does the Router support nested routes?
No. Nested route definitions (routes with a children property) are not supported. All routes must be defined as a flat array.
To achieve similar functionality, use shared layouts with flat routes:
const routes = [
{ path: "/dashboard", component: DashboardHome, layout: DashboardLayout },
{ path: "/dashboard/settings", component: Settings, layout: DashboardLayout },
{ path: "/dashboard/users/:id", component: UserDetail, layout: DashboardLayout }
];
See the Router Configuration guide for more details.
Q: How do I migrate from React to Eleva?
Migration involves:
useState with Eleva’s signal()useEffect with signal watchers or lifecycle hooksSee the React Migration Guide for detailed examples.
Q: How do I migrate from Vue to Eleva?
Migration involves:
See the Vue Migration Guide for detailed examples.
Q: How do I migrate from Alpine.js to Eleva?
Both share a similar philosophy—lightweight, no build step. Migration involves:
x-data with setup() + signal()x-show/x-if to ternary expressionsx-for with .map().join('')x-model to value + @input patternSee the Alpine.js Migration Guide for detailed examples.
Eleva has a comprehensive test suite ensuring reliability and stability.
| Metric | Value |
|---|---|
| Total Tests | 1,624 |
| Line Coverage | 100% |
| Function Coverage | 100% (core) |
| Test Runner | Bun |
# Run all tests
bun test
# Run with coverage report
bun test:coverage
# Run unit tests only
bun test test/unit
# Run performance benchmarks
bun test:benchmark
# Run prepublish checks (lint + test + build)
bun run prepublishOnly
test/
├── unit/ # Unit tests
│ ├── core/ # Core Eleva tests
│ │ └── Eleva.test.ts
│ ├── modules/ # Module tests
│ │ ├── Emitter.test.ts
│ │ ├── Renderer.test.ts
│ │ ├── Signal.test.ts
│ │ └── TemplateEngine.test.ts
│ └── plugins/ # Plugin tests
│ ├── Agent.test.ts
│ ├── Attr.test.ts
│ ├── Router.test.ts
│ └── Store.test.ts
└── performance/ # Performance benchmarks
├── fps-benchmark.test.ts
└── js-framework-benchmark.test.ts
Eleva uses Bun’s built-in test runner with a Jest-compatible API:
import { describe, test, expect, beforeEach } from "bun:test";
import Eleva from "../../src/index.js";
describe("MyComponent", () => {
let app: Eleva;
beforeEach(() => {
document.body.innerHTML = `<div id="app"></div>`;
app = new Eleva("TestApp");
});
test("should mount correctly", async () => {
const component = {
setup: ({ signal }) => ({ count: signal(0) }),
template: (ctx) => `<div>${ctx.count.value}</div>`
};
const instance = await app.mount(
document.getElementById("app")!,
component
);
expect(instance).toBeTruthy();
expect(document.body.innerHTML).toContain("0");
});
});
Problem: Component doesn’t appear in the DOM.
Solutions:
const container = document.getElementById("app");
if (!container) {
console.error("Container not found!");
}
app.mount(container, "MyComponent");
Problem: Signal value changes but UI doesn’t update.
Solutions:
.value to read signals in templates:
// Wrong
template: (ctx) => `<p>${ctx.count}</p>`
// Correct
template: (ctx) => `<p>${ctx.count.value}</p>`
.value, not the signal itself:
// Wrong
count = 5;
// Correct
count.value = 5;
Problem: @click or other event handlers don’t fire.
Solutions:
ctx. in event handlers:
// Wrong
@click="ctx.handleClick"
// Correct
@click="handleClick"
@click="() => count.value++"
Problem: Function runs when page loads instead of on click.
Cause: Calling the function with arguments without wrapping in arrow function.
Solution: Always wrap handlers with arguments in arrow functions:
// Wrong - executes immediately during render!
@click="remove(item.id)"
@click="setCount(5)"
// Correct - executes on click
@click="() => remove(item.id)"
@click="() => setCount(5)"
Note: Direct references work fine when no arguments are needed:
@click="handleClick"
Problem: Typing in an input doesn’t update the signal.
Solutions:
value attribute (not .value prefix):
// Wrong (Lit-specific syntax)
<input .value="${ctx.name.value}" />
// Correct
<input value="${ctx.name.value}" />
@input handler:
<input
value="${ctx.name.value}"
@input="(e) => name.value = e.target.value"
/>
Problem: Showing code like ${x.value} in templates displays the actual value instead of the code.
Cause: Template engine evaluates all ${...} expressions.
Solution: Use HTML entities to escape the $ character:
// Wrong - gets evaluated by template engine
<code>template: `Count: ${count.value}`</code>
// Correct - displays as literal code
<code>template: `Count: ${count.value}`</code>
| Character | HTML Entity |
|---|---|
$ |
$ |
< |
< |
> |
> |
Problem: Child component doesn’t receive props.
Solutions:
ctx. in :prop attributes:
// Wrong
:user="ctx.userData.value"
// Correct
:user="userData.value"
:prop expressions are evaluated, so primitive IDs work directly:
:postId="${post.id}"
children:
children: {
".child-selector": "ChildComponent"
}
Problem: Application becomes slow over time.
Solutions:
onUnmount:
setup: ({ signal }) => {
let interval = null;
return {
onMount: () => {
interval = setInterval(() => {}, 1000);
},
onUnmount: () => {
clearInterval(interval);
}
};
}
Note: If a parent re-render removes a child component, Eleva schedules child
onUnmountcleanup right after the DOM patch. Treat child cleanup as potentially async in those cases.
// Log signal changes (watcher receives new value only)
const count = signal(0);
count.watch((newVal) => {
console.log(`Count changed to: ${newVal}`);
});
// Debug template rendering
template: (ctx) => {
console.log("Rendering with context:", ctx);
return `<div>${ctx.count.value}</div>`;
}
// Check mounted instance
const instance = await app.mount(container, "MyComponent");
console.log("Mounted instance:", instance);
| Method | Description | Returns |
|---|---|---|
new Eleva(name) |
Create app instance | Eleva |
app.component(name, def) |
Register component | Eleva |
app.mount(el, name, props?) |
Mount to DOM | Promise<MountResult> |
app.use(plugin, options?) |
Install plugin | Eleva or plugin result |
| Method | Description |
|---|---|
signal(value) |
Create reactive state |
signal.value |
Read/write current value |
signal.watch(fn) |
Subscribe to changes |
| Method | Description |
|---|---|
emitter.on(event, fn) |
Subscribe to event |
emitter.off(event, fn) |
Unsubscribe from event |
emitter.emit(event, data) |
Emit event |
For detailed API documentation, see the individual module guides:
Join our community for support, discussions, and collaboration:
| Channel | Purpose | Link |
|---|---|---|
| GitHub Discussions | General questions, ideas | Discussions |
| GitHub Issues | Bug reports, feature requests | Issues |
| Stack Overflow | Technical questions | eleva, eleva.js |
| Community projects | r/elevajs | |
| Discord | Real-time chat | Join |
| Telegram | Support, feedback | Join |
Contributions are welcome! Whether you’re fixing bugs, adding features, or improving documentation, your input is invaluable.
See the CONTRIBUTING file for guidelines.
For a detailed log of all changes and updates, please refer to the Changelog.
Eleva is open-source and available under the MIT License.
| ← Best Practices | Back to Main Docs | Examples → |