Welcome to the official documentation for Eleva, a minimalist, lightweight, pure vanilla JavaScript frontend runtime framework. Whether you’re new to JavaScript or an experienced developer, this guide will help you understand Eleva’s core concepts, architecture, and how to integrate and extend it in your projects.
Eleva is designed to offer a simple yet powerful way to build frontend applications using pure vanilla JavaScript. Its goal is to empower developers who value simplicity, performance, and full control over their application to build modular and high-performance apps without the overhead of larger frameworks.
Eleva is unopinionated. Unlike many frameworks that enforce a specific project structure or coding paradigm, Eleva provides only the minimal core with a flexible plugin system, leaving architectural decisions in your hands. This means:
At the heart of Eleva are a few fundamental principles that guide its design and usage:
Minimalism:
Eleva includes only the essential features needed for building functional, high-performance applications without added complexity.
Reactivity:
With its signal-based reactivity, Eleva updates only the parts of the UI that change, ensuring smooth and efficient DOM updates.
Simplicity:
Built using pure vanilla JavaScript, Eleva offers a shallow learning curve and seamless integration with existing projects.
Modularity:
Each component is self-contained, making your application scalable and maintainable.
Flexibility:
Eleva’s unopinionated nature allows you to choose your own architectural patterns and extend the framework with plugins as needed.
Performance:
Designed to be lightweight and efficient, Eleva is ideal for performance-critical applications.
Preliminary benchmarks illustrate Eleva’s efficiency compared to popular frameworks:
Framework | Bundle Size (KB) | Initial Load Time (ms) | DOM Update Speed (s) | Peak Memory Usage (KB) | Overall Performance Score (lower is better) |
---|---|---|---|---|---|
Eleva (Direct DOM) | 1.8 | 10 | 0.018 | 0.25 | 3.02 (Best) |
React (Virtual DOM) | 42 | 40 | 0.020 | 0.25 | 20.57 |
Vue (Reactive State) | 33 | 35 | 0.021 | 3.10 | 17.78 |
Angular (Two-way Binding) | 80 | 100 | 0.021 | 0.25 | 45.07 (Slowest) |
Detailed Benchmark Metrics Report
⚠️ Disclaimer: Benchmarks are based on internal tests and may vary by project and environment.
Install via npm:
npm install eleva
Or include via CDN:
<!-- jsDelivr (Recommended) -->
<script src="https://cdn.jsdelivr.net/npm/eleva"></script>
or
<!-- unpkg -->
<script src="https://unpkg.com/eleva/dist/eleva.min.js"></script>
Below is a step-by-step tutorial to help you get started. This example demonstrates component registration, state creation, and mounting using a DOM element (not a selector), with asynchronous handling.
import Eleva from "eleva";
const app = new Eleva("MyApp");
// Define a simple component
app.component("HelloWorld", {
// Optional setup: if omitted, Eleva defaults to an empty state
setup({ signal }) {
const count = signal(0);
return { count };
},
template: (ctx) => `
<div>
<h1>Hello, Eleva! 👋</h1>
<p>Count: ${ctx.count.value}</p>
<button @click="() => count.value++">Increment</button>
</div>
`,
});
// Mount the component by providing a DOM element and handling the returned Promise
app
.mount(document.getElementById("app"), "HelloWorld")
.then((instance) => console.log("Component mounted:", instance));
For interactive demos, check out the CodePen Example.
The TemplateEngine is responsible for parsing templates and evaluating embedded expressions.
parse(template, data)
: Replaces `` with values from data
.evaluate(expr, data)
: Safely evaluates JavaScript expressions within the provided context.Example:
const template = "Hello, !";
const data = { name: "World" };
const output = TemplateEngine.parse(template, data);
console.log(output); // "Hello, World!"
Eleva supports two methods for dynamic content:
${...}
):Example:
const greeting = `Hello, ${name}!`; // Evaluates to "Hello, World!" if name is "World"
<p>Hello, !</p>
When to Use Each:
${...}
for one-time, static content.Understanding how data flows during component initialization and event handling is key:
setup
function during initialization.signal
function), component props, emitter, and lifecycle hooks. The returned data forms the component’s reactive state.Example:
const MyComponent = {
setup: ({ signal }) => {
const counter = signal(0);
return { counter };
},
template: (ctx) => `
<div>
<p>Counter: ${ctx.counter.value}</p>
</div>
`,
};
setup
along with event-specific data (like event.target
).Example:
const MyComponent = {
setup: ({ signal }) => {
const counter = signal(0);
function increment(event) {
console.log("Event type:", event.type);
counter.value++;
}
return { counter, increment };
},
template: (ctx) => `
<div>
<p>Counter: ${ctx.counter.value}</p>
<button @click="increment">Increment</button>
</div>
`,
};
The Signal provides fine-grained reactivity by updating only the affected DOM parts.
new Signal(initialValue)
creates a reactive data holder.signal.value
.signal.watch(callback)
registers a function to execute on changes.Example:
const count = new Signal(0);
count.watch((newVal) => console.log("Count updated:", newVal));
count.value = 1; // Logs: "Count updated: 1"
The Emitter enables inter-component communication through events and using a publish–subscribe pattern.
on(event, handler)
: Registers an event handler.off(event, handler)
: Removes an event handler.emit(event, ...args)
: Emits an event with optional arguments.Example:
on("greet", (name) => console.log(`Hello, ${name}!`)); // Logs: "Hello, Alice!"
emit("greet", "Alice");
The Renderer efficiently updates the DOM through diffing and patching.
patchDOM(container, newHtml)
: Updates container content with the new HTML.diff(oldParent, newParent)
: Applies updates by comparing DOM trees.updateAttributes(oldEl, newEl)
: Synchronizes element attributes.The Eleva class orchestrates component registration, mounting, plugin integration, lifecycle management, and events.
new Eleva(name, config)
: Creates an instance.use(plugin, options)
: Integrates a plugin.component(name, definition)
: Registers a new component.mount(container, compName, props)
: Mounts a component to a DOM element (returns a Promise).Execute code at various stages of a component’s lifecycle:
Example:
const MyComponent = {
setup: ({ signal, onMount, onUnmount }) => {
const counter = signal(0);
onMount(() => console.log("Component mounted"));
onUnmount(() => console.log("Component unmounted"));
return { counter };
},
template: (ctx) => `<div>Counter: ${ctx.counter}</div>`,
};
Register components globally or directly, then mount using a DOM element.
Example (Global Registration):
const app = new Eleva("MyApp");
app.component("HelloWorld", {
setup({ signal }) {
const count = signal(0);
return { count };
},
template: (ctx) => `
<div>
<h1>Hello, Eleva! 👋</h1>
<p>Count: ${ctx.count.value}</p>
<button @click="() => count.value++">Increment</button>
</div>
`,
});
app.mount(document.getElementById("app"), "HelloWorld").then((instance) => {
console.log("Component mounted:", instance);
});
Example (Direct Component Definition):
const DirectComponent = {
template: () => `<div>No setup needed!</div>`,
};
const app = new Eleva("MyApp");
app
.mount(document.getElementById("app"), DirectComponent)
.then((instance) => console.log("Mounted Direct:", instance));
Eleva allows you to nest components. A parent can include child components in its template and pass data to them via props.
Attributes prefixed with eleva-prop-
are automatically parsed as props.
Example:
// Define the Child Component
const ChildComponent = {
setup: (context) => {
// Access props from the context
const { title } = context.props;
return { title };
},
template: (ctx) => `
<div>
<p>Child Component Title: ${ctx.title}</p>
</div>
`,
};
// Define the Parent Component with local child registration
const ParentComponent = {
template: () => `
<div>
<h1>Parent Component</h1>
<child-comp eleva-prop-title="Hello from Parent"></child-comp>
</div>
`,
children: {
"child-comp": ChildComponent,
},
};
const app = new Eleva("TestApp");
app.component("parent-comp", ParentComponent);
app.mount(document.getElementById("app"), "parent-comp");
Eleva supports component-scoped styling through an optional style
function defined in a component.
The styles are injected into the component’s container to avoid global leakage.
Example:
const MyComponent = {
style: (ctx) => `
.my-component {
color: blue;
padding: rem;
}
`,
template: (ctx) => `<div class="my-component">Styled Component</div>`,
};
Inter-component communication is facilitated by the built-in Emitter. Components can publish and subscribe to events, enabling decoupled interactions.
Example:
// Component A emits an event
app.component("ComponentA", {
setup: ({ emitter }) => {
function sendMessage() {
emitter.emit("customEvent", "Hello from A");
}
return { sendMessage };
},
template: () => `<button @click="sendMessage">Send Message</button>`,
});
// Component B listens for the event
app.component("ComponentB", {
setup: ({ emitter }) => {
emitter.on("customEvent", (msg) => console.log(msg));
return {};
},
template: () => `<div>Component B</div>`,
});
app.mount(document.getElementById("app"), "ComponentA");
app.mount(document.getElementById("app"), "ComponentB");
Eleva’s design emphasizes clarity, modularity, and performance. This section explains how data flows through the framework and how its key components interact, providing more clarity on the underlying mechanics.
Component Definition:
Components are plain JavaScript objects that describe a UI segment. They typically include:
template
function that returns HTML with interpolation placeholders.setup()
function for initializing state (using reactive signals).style
function for scoped CSS.children
object for nested components.Signals (Reactivity): Signals are reactive data holders that notify watchers when their values change, triggering re-renders of the affected UI.
TemplateEngine (Rendering):
This module processes template strings by replacing placeholders (e.g., 8
) with live data, enabling dynamic rendering.
Renderer (DOM Diffing and Patching): The Renderer compares the new virtual DOM with the current DOM and patches only the parts that have changed, ensuring high performance and efficient updates.
Emitter (Event Handling): The Emitter implements a publish–subscribe pattern to allow components to communicate by emitting and listening to custom events.
Initialization:
app.component()
.app.mount()
creates a context (including props, lifecycle hooks, and an emitter
property) and executes setup()
(if present) to create a reactive state.Rendering:
8
with the current values.Reactivity:
Events:
@click
) during rendering.[Component Registration]
│
▼
[Mounting & Context Creation]
│
▼
[setup() Execution]
│
▼
[Template Function Produces HTML]
│
▼
[TemplateEngine Processes HTML]
│
▼
[Renderer Patches the DOM] ◂────────┐
│ │
▼ │
[User Interaction / Signal Change] │
│ │
▼ │ ↺
[Signal Watchers Trigger Re-render] │
│ │
▼ │
[Renderer Diffs the DOM] ─────────┘
A plugin is an object with an install
method:
const MyPlugin = {
install(eleva, options) {
// Extend Eleva functionality here
eleva.myPluginFeature = function () {
// Plugin logic
};
},
};
export default MyPlugin;
Plugins can:
This modular approach makes Eleva highly customizable.
console.log
in lifecycle hooks and event handlers.Explore these guides for real-world examples:
Interactive demos are also available on Eleva’s CodePen Collection for you to experiment live.
Q: Is Eleva production-ready? A: Not yet—Eleva is currently in alpha. While it’s a powerful tool, expect changes until a stable release is announced.
Q: How do I report issues or request features? A: Please use the GitHub Issues page.
Q: Can I use Eleva with TypeScript? A: Absolutely! Eleva includes built-in TypeScript declarations to help keep your codebase strongly typed.
Common Issues:
mount()
.Detailed API documentation with parameter descriptions, return values, and usage examples can be found in the docs folder.
TemplateEngine:
parse(template, data)
and evaluate(expr, data)
Signal:
new Signal(value)
, getter/setter for signal.value
, and signal.watch(fn)
Emitter:
Methods: on(event, handler)
, off(event, handler)
, and emit(event, ...args)
Renderer:
Methods: patchDOM(container, newHtml)
, diff(oldParent, newParent)
, and updateAttributes(oldEl, newEl)
Eleva (Core):
new Eleva(name, config)
, use(plugin, options)
, component(name, definition)
, and mount(container, compName, props)
Note: In v1.2.0-alpha, the mounting context now includes an
emitter
property (instead of separateemit
andon
functions). Usecontext.emitter.on(...)
andcontext.emitter.emit(...)
for event handling.
Contributions are welcome! Whether you’re fixing bugs, adding features, or improving documentation, your input is invaluable. Please checkout the CONTRIBUTING file for detailed guidelines on how to get started.
Join our community for support, discussions, and collaboration:
For a detailed log of all changes and updates, please refer to the Changelog.
Latest Update (v1.2.2-alpha):
- Documentation Updates
Eleva is open-source and available under the MIT License.
Thank you for exploring Eleva! I hope this documentation helps you build amazing, high-performance frontend applications using pure vanilla JavaScript. For further information, interactive demos, and community support, please visit the GitHub Discussions page.