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 🙏

© 2026 – Pkg Stats / Ryan Hefner

holo-js

v1.1.0

Published

The new react killer.

Readme

Banner

A lightweight, component-based JavaScript framework for building dynamic web applications with minimal boilerplate. Holo.js offers reactive state management, custom component registration, and flexible routing in a simple API.

  • Now with DevTools and Error Boundaries!

Contributors

Features

  • Reactive State Management: Auto-updating UI based on state changes
  • Custom Components: Create reusable web components with simple API
  • Template Binding: Easy templating with state variable interpolation
  • Flexible Routing: Support for both hash and history routing modes
  • Route Guards: Protect routes with custom logic
  • Persistent Storage: Simple localStorage API integration
  • Minimal Size: Lightweight framework with no dependencies
  • Progressive Web App Ready: Built with modern web standards

Installation

  • Install it from NPM:

    npm i holo-js
  • Use it!

Quick Start

import { Holo, Router } from 'holo-js';

// Initialize the application
const app = Holo.init();

// Set application title
app.setTitle('My Holo App');

// Create state
app.state.counter = 0;
app.state.username = 'Guest';

// Define a component
app.registerComponent({
  name: 'counter-button',
  render: (attrs, inner) => `
    <button onclick="incrementCounter()">
      ${inner || 'Clicks'}: {counter}
    </button>
  `
});

// Global function
app.expose(function incrementCounter() {
  app.state.counter++;
});

// Render the main template
app.render(`
  <div id="app-content"></div>
  <div class="container">
    <h1>{username}'s App</h1>
    <counter-button>Count</counter-button>
  </div>
`);

// Setup router (optional)
const router = new Router({ mode: 'hash' })
  .setHoloInstance(app)
  .setContainer('#app-content')
  .add('/', '<h1>Home</h1><p>Welcome, {username}!</p>')
  .add('/about', '<h1>About</h1><p>This is a Holo app</p>')
  .setNotFoundTemplate('<h1>404</h1><p>Page not found</p>')
  .init();

Core Concepts

Application Instance

The core of every Holo application is an instance created with Holo.init():

const app = Holo.init();

State Management

Holo uses a reactive state system that automatically updates the UI:

// Define state
app.state.count = 0;
app.state.user = { name: 'Guest' };

// Update state (UI updates automatically)
app.state.count++;
app.state.user.name = 'John';

// Subscribe to state changes
app.subscribeToState('count', (newValue, oldValue) => {
  console.log(`Count changed from ${oldValue} to ${newValue}`);
});

Component Registration

Create reusable components with simple registration:

app.registerComponent({
  name: 'user-card',
  render: (attrs, inner) => `
    <div class="card">
      <h3>{user.name}</h3>
      <div class="content">${inner}</div>
      <button onclick="greet('{user.name}')">Say Hello</button>
    </div>
  `
});

// Use the component
app.render(`
  <user-card>This is a user profile</user-card>
`);

Templating

Templates use simple curly brace syntax for state interpolation:

app.render(`
  <div>
    <h1>Welcome, {user.name}!</h1>
    <p>You have {notifications.length} notifications.</p>
  </div>
`);

Routing

The Router provides navigation and content swapping:

const router = new Router({ mode: 'hash' }) // or 'history'
  .setHoloInstance(app)
  .setContainer('#content')
  .add('/', homeTemplate)
  .add('/users', usersTemplate)
  .add('/users/:id', userDetailTemplate)
  .addGuard((from, to) => {
    // Return false to prevent navigation
    // Return string to redirect
    // Return undefined/true to continue
    if (to === '/admin' && !app.state.isAdmin) {
      return '/login';
    }
  })
  .init();

// Navigate programmatically
router.navigate('/users');

Local Storage

Holo includes a simple storage API that is based on localStorage:

// Store data
app.saves.set('user-prefs', { theme: 'dark', fontSize: 16 });

// Retrieve data
const prefs = app.saves.get('user-prefs');

// Remove item
app.saves.rm('user-prefs');

// Clear all app data
app.saves.clear();

API Reference

Holo Class

| Method | Description | |--------|-------------| | Holo.init() | Creates and initializes a new Holo application | | setTitle(title) | Sets the document title | | registerComponent(component) | Registers a custom component | | render(template, targetSelector) | Renders a template to a DOM element | | onReady(callback) | Executes code when DOM is loaded | | expose(fn, name) | Exposes a function globally | | subscribeToState(prop, callback) | Subscribes to state changes |

Router Class

| Method | Description | |--------|-------------| | new Router(options) | Creates a new router instance | | setHoloInstance(app) | Connects router to Holo instance | | setContainer(selector) | Sets the container for route content | | add(path, template) | Adds a route with content template | | setNotFoundTemplate(template) | Sets 404 page template | | addGuard(guardFn) | Adds a navigation guard function | | init() | Initializes the router | | navigate(path) | Navigates to a specific route |

StorageManager Class

| Method | Description | |--------|-------------| | set(key, value) | Stores data in localStorage | | get(key) | Retrieves data from localStorage | | rm(key) | Removes data from localStorage | | clear() | Clears all app data from localStorage |

Best Practices

  1. Organize Components: Create components for reusable UI elements
  2. State Structure: Keep state organized with logical grouping
  3. Event Handling: Use exposed functions for event handling
  4. Route Organization: Group related routes for easier maintenance
  5. Error Handling: Add error boundaries for component failures

Examples

Todo App

import { Holo } from 'holo-js';

const app = Holo.init().setTitle('Holo Todo');

// Initial state
app.state.todos = [];
app.state.newTodo = '';

// Components
app.registerComponent({
  name: 'todo-item',
  render: (attrs, inner) => `
    <li>
      <input type="checkbox" onclick="toggleTodo(${inner})" 
        ${app.state.todos[inner].done ? 'checked' : ''}>
      <span style="${app.state.todos[inner].done ? 'text-decoration: line-through' : ''}">
        ${app.state.todos[inner].text}
      </span>
      <button onclick="removeTodo(${inner})">×</button>
    </li>
  `
});

// Global functions
app.expose(function addTodo() {
  if (app.state.newTodo.trim()) {
    app.state.todos = [...app.state.todos, { text: app.state.newTodo, done: false }];
    app.state.newTodo = '';
  }
});

app.expose(function removeTodo(index) {
  app.state.todos = app.state.todos.filter((_, i) => i !== index);
});

app.expose(function toggleTodo(index) {
  const newTodos = [...app.state.todos];
  newTodos[index].done = !newTodos[index].done;
  app.state.todos = newTodos;
});

app.expose(function updateInput(e) {
  app.state.newTodo = e.target.value;
});

// Render app
app.render(`
  <div class="todo-app">
    <h1>Holo Todo</h1>
    <div class="add-todo">
      <input type="text" value="{newTodo}" onkeyup="updateInput(event)" 
        onkeypress="if(event.key==='Enter')addTodo()">
      <button onclick="addTodo()">Add</button>
    </div>
    <ul>
      ${'{todos.map((_, i) => `<todo-item>${i}</todo-item>`).join("")}'}
    </ul>
    <div class="info">
      <p>{todos.length} items, {todos.filter(t => t.done).length} completed</p>
    </div>
  </div>
`);

Browser Support

Holo.js supports all modern browser engines that implement the Web Components standard:

  • Chromium
  • Gecko
  • WebKit
  • (might support more, but we haven't tested)

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the GNU General Public License v3.0

see the LICENSE file for details.