@ankabit/rui
v1.0.5
Published
A lightweight, reactive, template-driven web component framework with hot-reloadable templates and plugin system
Maintainers
Readme
📖 RUI (Reusable UI) Framework — Comprehensive Developer Documentation
Welcome to the RUI frontend framework documentation — a lightweight, reactive, template-driven web component system built for modern, scalable frontend applications.
Important: RUI is built to run without requiring a build step or bundler. You can drop rui.js or rui.min.js directly into your project and start using it immediately in your browser, or optionally use a modern dev server like Vite for hot reload convenience.
📚 Overview
RUI offers a minimal but extensible system for building and managing Web Components using native browser APIs, supporting reactive state management, hot-reloadable templates, plugin hooks, and pluggable template engines (Handlebars, Mustache, or custom functions).
Features:
- Lightweight web component base class.
- Hot-reloadable template system (inline or remote).
- Reactive state.
- Plugin management with priority queue, versioning, and enable/disable support.
- SPA-friendly and router-agnostic.
- Zero build tools required — works natively in the browser.
⚙️ Getting Started
1️⃣ Include RUI In Your Project
You can either:
- Use via CDN:
<script src="https://unpkg.com/@ankabit/[email protected]/dist/rui.min.js"></script>- Or download
rui.js/rui.min.jsfrom the repo and include locally.
Example Project Structure:
/my-rui-project/
/templates/
/src/
rui.min.js
index.htmlImportant: No bundler is required — RUI is native ES module compatible.
2️⃣ Dev Server & Hot Reload Setup Options
🔥 Option 1: Vite Dev Server (Recommended)
Install Vite locally:
npm install vite --save-devAdd a dev script in package.json:
{
"scripts": {
"dev": "vite"
}
}Create vite.config.js if desired:
export default {
root: '.',
server: {
open: true,
port: 3000,
},
};Run your dev server:
npm run devNotes:
- Ensure your
index.htmlis in the project root. - Templates fetched by RUI must be inside the dev server’s accessible directory.
- Vite hot reloads JS modules, template fetches, and static assets.
🔥 Option 2: Native Browser with Live Server Extension (Zero Config)
- Install the Live Server VS Code extension (or any browser live reload plugin).
- Open your project folder and click Go Live.
- Your
index.htmland templates are served overlocalhost. - No NPM, no build tools.
🔥 Option 3: Simple Static HTTP Server
- Install a basic static HTTP server:
npm install -g http-server- Serve your project:
http-server . -p 3000Benefit: Zero config, zero build steps. Great for demos or local development.
📦 Defining Components
Base Component Class
Extend RUI.BaseComponent to create new custom components:
class AlertBox extends RUI.BaseComponent {
static tag = 'alert-box';
getInitialData() {
return {
message: 'Hello World!',
extraMessage: '', // This will automatically be set to the atribute passed at first i.e extra-message
};
}
onConnected() {
console.log('AlertBox connected!', this.data.extraMessage, this.getAttribute('extra-message'));
}
}
RUI.ComponentRegistry.register(AlertBox);Usage:
<rui-alert-box extra-message="Some text"></rui-alert-box>kebab case attributes are auto read as camel case i.e extra-message will be used in JS scope as extraMessage . This will be handled automatically by the library.
🎨 Templates
Inline Template:
<script type="text/template" data-tag-template="alert-box">
<div class='alert'>{{message}}</div>
</script>Remote Template:
Save as /templates/alert-box.hbs:
<div class="alert">{{message}}</div>Custom Template URL Resolution
You can customize how templates are resolved by providing a templateUrlResolver function:
RUI.Config.templateUrlResolver = async (key) => {
// Custom logic to resolve template URLs
return [`/custom-templates/${key}.hbs`, `/fallback/${key}.html`];
};The resolver function receives the template key and should return an array of URLs to try in order. If no resolver is provided, RUI will use the default behavior of searching through templateFolders with the configured extensions.
📌 Where to Place the Sanitizer Section in the README
I recommend placing it after the "Template Engine" section and before "Plugin Manager" in your README.
Why? Because:
- Template parsing happens before plugin notifications
- Sanitizing concerns the output of template rendering
- It's part of configuring the rendering process, like setting
templateEngine
📖 📂 Table of Contents
# RUI Documentation
- Introduction
- Getting Started
- Configuration
- Template Engine
- 🔒 Template Sanitizing (this new section)
- Plugin Manager
- BaseComponent API
- Hot Reloading
- Example Projects
- Development Setup
- Contributing📑 📦 Sanitizing Rendered Templates
🔒 Template Sanitizing
By default, RUI does not sanitize rendered templates to give you full control over security strategy for your environment.
To prevent XSS attacks or unsafe HTML rendering, you can configure a sanitizer function via RUI.Config.templateSanitizer and or via RUI.Config.renderedTemplateSanitizer.
The former will sanitize loaded template from inline script or URL and later will santizer rendering engine output.
If configured, this function will automatically process every rendered template string before it's inserted into the DOM.
📌 Configuring a Sanitizer
Example 1: Using DOMPurify
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/purify.min.js"></script>
<script>
RUI.Config.templateSanitizer = (html) => DOMPurify.sanitize(html);
</script>Example 2: Custom Regex Sanitizer
(Note: Regex sanitizers can be brittle — use with care)
RUI.Config.templateSanitizer = (html) => html.replace(/<script.*?>.*?<\/script>/gi, '');Example 3: Disable Sanitizing
If your templates are trusted or pre-sanitized:
RUI.Config.templateSanitizer = null;Always use RUI.Config.renderedTemplateSanitizer if you are parsing content from user input for safety.
📌 When Does the Sanitizer Run?
The sanitizer function runs after the template is loaded of fectched, after the template engine renders HTML, but before it’s inserted into the DOM:
Template → Load → Sanitizer(RUI.Config.templateSanitizer) → Render (Handlebars/Mustache/custom) → Sanitizer(RUI.Config.renderedTemplateSanitizer) → Inject into DOMThe later RUI.Config.renderedTemplateSanitizer ensures even partials or user-provided data are filtered safely.
📌 Tip: You Can Also Sanitize via a Plugin
If you'd prefer a plugin-based approach:
RUI.PluginManager.register({
name: 'SanitizerPlugin',
priority: 0, // we want this to be the last to run
onRegister: () => {
RUI.PluginManager.on('template:loaded', (html) => DOMPurify.sanitize(html));
},
});📌 Recommendation
If your app handles untrusted template content (user submissions, CMS-generated, etc), always enable sanitizing via one of these approaches.
🔄 Hot Reloading
To reload all templates:
RUI.reloadAllComponents();Or for a specific tag:
RUI.PluginManager.hotReload('rui-alert-box');🔌 Plugin API
Plugins can hook into component lifecycle events.
Example Logger Plugin:
RUI.PluginManager.register({
name: 'LoggerPlugin',
version: '1.0.0',
priority: 10,
onComponentConnected(component) {
console.log(`[Connected] ${component.constructor.tagName}`);
},
onComponentDisconnected(component) {
console.log(`[Disconnected] ${component.constructor.tagName}`);
},
});Managing Plugins
RUI.PluginManager.disable('LoggerPlugin');
RUI.PluginManager.enable('LoggerPlugin');
RUI.PluginManager.list();🔍 DOM Utilities
qs(selector)— querySelector within component root.qsa(selector)— querySelectorAll within component root.
🖥️ SPA & Routing
RUI is router-agnostic. You can use any lightweight router like Navigo or tiny-router.
Example SPA Setup:
const router = new Navigo('/');
router
.on('/about', () => {
document.querySelector('#view').innerHTML = '<rui-about-page></rui-about-page>';
})
.resolve();📦 Nested Components Example
<rui-card>
<rui-button label="Click me!"></rui-button>
</rui-card>Each can render individually with their own templates and data.
📝 Real-world Mini Project Idea
A simple SPA dashboard:
/templates/default/dashboard.hbs<rui-navbar>with inline template.<rui-dashboard>with cards and buttons.- Navigo for routing.
📖 Full API Reference
RUI.Config
tagPrefix- Custom element tag prefix (default: 'rui')templateFolders- Array of folder paths to search for templates (default: ['/templates'])templateUrlResolver- Optional function to customize template URL resolution (default: null)extensions- Array of allowed template file extensions (default: ['.hbs'])templateEngine- Template engine ('handlebars', 'mustache', 'liquidjs', or custom function)debug- Enable debug logging (default: false)templateSanitizer- Function to sanitize loaded templates (default: null)renderedTemplateSanitizer- Function to sanitize rendered templates (default: null)
RUI.ComponentRegistry
register(componentClass)
RUI.TemplateLoader
load(key)normalizeKey(tagName)
RUI.TemplateEngine
parse(template, data, partials)
RUI.PluginManager
register(plugin)notifyConnected(component)notifyDisconnected(component)hotReload(tagName?)enable(name)disable(name)list()
RUI.BaseComponent
tagtemplateNamespacestatic get observedAttributes()getInitialData()getPartials()getTemplateKey()parseTemplate(template, data, partials)onBeforeConnected()onConnected()onAttributeChanged(name, oldValue, newValue)onBeforeDisconnected()onDisconnected()reloadTemplate()qs(selector)qsa(selector)bindEvent(el, type, handler)bindRootEvent(type, handler)unbindAllEvents()emit(event, detail)on(event, handler)off(event, handler)
Global
RUI.reloadAllComponents()
🙏 Acknowledgments
RUI draws inspiration from several excellent frameworks and libraries:
- Lit - Web Components best practices and modern browser APIs
- Ractive - Template-driven development and data binding concepts
- Reef - Lightweight reactive rendering and simple API design
- React - Component-based architecture and reactive state management patterns
We're grateful to these projects and their communities for paving the way for modern frontend development.
📌 Final Notes
- Works with Handlebars, Mustache, LiquidJS (via window.liquidEngine), or any custom template parser.
- Zero dependencies.
- Plugin system is priority-aware and can be toggled.
- Uses Light DOM for simple, accessible components.
- No build tool required — use native browser modules.
- Dev server is optional for hot reloading.
