npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@ankabit/rui

v1.0.5

Published

A lightweight, reactive, template-driven web component framework with hot-reloadable templates and plugin system

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.js from the repo and include locally.

Example Project Structure:

/my-rui-project/
  /templates/
  /src/
  rui.min.js
  index.html

Important: 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-dev

Add 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 dev

Notes:

  • Ensure your index.html is 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.html and templates are served over localhost.
  • 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 3000

Benefit: 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 DOM

The 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

  • tag
  • templateNamespace
  • static 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.