Version: 1.2.0 Complete guide to creating, testing, and publishing Eleva plugins.
Plugin development requires a solid understanding of Eleva’s architecture:
Here’s a complete, minimal example of creating and registering a custom plugin:
import Eleva from "eleva";
// 1. Define the plugin
const UtilsPlugin = {
name: "utils", // Required: unique identifier
version: "1.0.0", // Required: semantic version string
install(eleva, options = {}) { // Required: called when plugin is registered
// Add utility methods to the Eleva instance
eleva.utils = {
formatDate: (date) => new Date(date).toLocaleDateString(),
capitalize: (str) => str.charAt(0).toUpperCase() + str.slice(1),
debounce: (fn, delay = 300) => {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
};
}
};
// 2. Create Eleva instance
const app = new Eleva("MyApp");
// 3. Register the plugin with app.use()
app.use(UtilsPlugin); // Without options
// OR
app.use(UtilsPlugin, { locale: "en-US" }); // With options
// 4. Use the plugin in components
app.component("DateDisplay", {
setup: () => ({
formatted: app.utils.formatDate(Date.now()) // Access via app instance
}),
template: (ctx) => `<p>Today: ${ctx.formatted}</p>`
});
app.mount(document.getElementById("app"), "DateDisplay");
Before you start, ensure you have:
An Eleva plugin is a JavaScript object that extends the framework’s functionality. Every plugin must have:
name property (string)version property following semantic versioning (string)install method that receives the Eleva instance and optionsconst MyPlugin = {
name: "myPlugin",
version: "1.0.0",
install(eleva, options) {
// Plugin implementation
}
};
| Property | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Unique identifier for the plugin |
version |
string |
Yes | Semantic version (e.g., “1.0.0”, “2.1.3”) |
install |
function |
Yes | Called with (eleva, options) during registration |
uninstall |
function |
No | Optional cleanup when plugin is removed |
Plugins are registered using the app.use() method. This method calls the plugin’s install function with the Eleva instance and any provided options.
import Eleva from "eleva";
import MyPlugin from "./my-plugin.js";
const app = new Eleva("MyApp");
// Register without options
app.use(MyPlugin);
// Register with options
app.use(MyPlugin, { option1: "value1", option2: true });
Plugins are installed in the order they are registered. If plugins depend on each other, register dependencies first:
// Logger should be registered before Analytics (Analytics uses Logger)
app.use(LoggerPlugin);
app.use(AnalyticsPlugin); // Can now use app.log from LoggerPlugin
After registration, plugin features are available on the Eleva instance:
// In your plugin
install(eleva, options) {
eleva.myFeature = { /* ... */ };
}
// After registration
app.use(MyPlugin);
console.log(app.myFeature); // Available immediately
// In components via the app instance
app.component("MyComponent", {
setup: () => ({
doSomething: () => app.myFeature.someMethod()
}),
template: (ctx) => `<button @click="doSomething">Click</button>`
});
The app.use() method returns the result of plugin.install(). Use this for plugins that expose an API:
// Plugin that returns an API
const RouterPlugin = {
name: "router",
version: "1.0.0",
install(eleva, options) {
const router = new Router(eleva, options);
eleva.router = router;
return router; // Return for direct access
}
};
// Usage
const router = app.use(RouterPlugin, { mount: "#app", routes: [...] });
await router.start();
// The returned router instance provides direct API access
plugin.install(eleva, options)install() is returned to the callerPlugins can implement an optional uninstall() method for proper cleanup. This is recommended when your plugin wraps existing methods or adds properties to the Eleva instance.
const MyPlugin = {
name: "myPlugin",
version: "1.0.0",
install(eleva, options) {
// Store original method for later restoration
const originalMount = eleva.mount;
eleva._myPlugin_originalMount = originalMount;
// Wrap the method
eleva.mount = function(...args) {
console.log("Enhanced mount");
return originalMount.call(this, ...args);
};
// Add new properties
eleva.myFeature = { /* ... */ };
},
uninstall(eleva) {
// Restore original method
if (eleva._myPlugin_originalMount) {
eleva.mount = eleva._myPlugin_originalMount;
delete eleva._myPlugin_originalMount;
}
// Remove added properties
delete eleva.myFeature;
// Remove from plugin registry
if (eleva.plugins) {
eleva.plugins.delete(this.name);
}
}
};
// Usage
app.use(MyPlugin);
// Later, to uninstall:
MyPlugin.uninstall(app);
| Resource | Cleanup Action |
|---|---|
| Wrapped methods | Restore to original functions |
| Added properties | Delete from Eleva instance |
| Event listeners | Remove all subscriptions |
| Timers/intervals | Clear all timers |
| Plugin registry entry | Remove from eleva.plugins Map (if you added metadata) |
When multiple plugins wrap the same methods, uninstall in reverse order (Last In, First Out):
// Installation order
app.use(PluginA); // Wraps mount first
app.use(PluginB); // Wraps mount second
// Uninstall in reverse order
PluginB.uninstall(app); // Uninstall last plugin first
PluginA.uninstall(app); // Then earlier plugins
Note: See the Plugin System documentation for details on how plugin cleanup differs from component cleanup.
Study how Eleva’s core plugins are built:
| Plugin | What You’ll Learn |
|---|---|
| Attr Plugin | Attribute handling, configuration options (API) |
| Router Plugin | Complex state management, navigation (Configuration · Guards) |
| Store Plugin | Reactive state, persistence, namespacing (Patterns · Advanced) |
| ← Back to Examples | Development Guide → |