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

@vanilla-bean/components

v2.0.1

Published

A lightweight, reactive component library for building modern web applications with vanilla JavaScript

Readme

@vanilla-bean/components

A lightweight, reactive component library for building modern web applications with vanilla JavaScript. No build steps, no framework lock-in, no virtual DOM complexity.

Key Features

  • No build step for consumers - Pure ES modules; import directly in browsers or bundle with any tool
  • Reactive state management - Proxy-based, explicit; component.options is reactive out of the box, no dependency tracking magic
  • Scoped CSS styling - Opinionated dark theme by design; override per-instance, per-class, or replace the theme entirely
  • Cleanup on disconnect - no listener accumulation; components clean up after themselves
  • 25 production-ready components - A second tier of building blocks above the primitives. Button handles keyboard activation and pointer events. Dialog handles focus management and modal behavior. Form handles validation. These are composed concerns your app components build on, not reinvent.
  • Modern browser APIs - EventTarget, Proxy, ES6 modules, native CSS nesting
  • Framework-agnostic - works alongside Web Components, React islands, or any other approach; no conflicts, no opinions on your stack

Philosophy

Most UI frameworks insert themselves between your code and the DOM. VDOM reconcilers, reactive dependency graphs, compile-time transforms. Each layer runs so you don't have to think about it. The trade sounds appealing until something breaks: you're debugging the framework's decisions, in execution paths that aren't yours, with failure modes that are subtle by design.

VBC is built on a different premise. The DOM is stateful, and VBC accepts that and works with it directly. When data changes, you decide what updates, through _setOption handlers you write. Structure lives in build(). Data flows in through options. Updates happen exactly where you put them. No reconciliation loop. No dependency graph. No rerender cascade.

This is a position, not a limitation. Automatic systems don't eliminate complexity. They relocate it to places that are harder to reach when they break. VBC's failures are obvious: you forgot a key in _setOption and the UI doesn't update. You can see that immediately. The subtle failures, stale closures, dependency cycles, batching surprises, concurrent mode edge cases, belong to a different model entirely.

Table of Contents

Installation

Install from GitHub:

# Using bun
bun add github:fatlard1993/vanilla-bean-components

# Using npm
npm install github:fatlard1993/vanilla-bean-components

# Using yarn
yarn add github:fatlard1993/vanilla-bean-components

Reactive state (Oxject, derive) is included in VBC, no separate install. The HTTP examples use hypertether, which is a separate package:

bun add @vanilla-bean/hypertether

Import in your application:

import { Component, Oxject, styled } from '@vanilla-bean/components';

// Specific components
import { Button, Dialog, Calendar } from '@vanilla-bean/components';

Interactive Explorer

bun start launches the component explorer at http://localhost:9999 (clone first, see Development):

  • Live option editor: every component, every option, type-appropriate controls, updated in real time
  • Auto-generated API docs: options tables, method signatures, event listings pulled directly from source annotations
  • 11 real example applications: async data tables with row actions, localStorage-backed todo, a live HTML/JS/CSS playground with iframe preview, a drawing canvas with a functioning in-game economy
  • Ancestor chain navigation: each component shows its full inheritance (EventTarget → Elem → Component → ...) with links to each layer's documentation

The explorer is built with VBC itself. It uses the same primitives and patterns you'd use in your own app.

Quick Start

Basic Component Creation

Every component extends EventTarget and wraps an HTMLElement:

import { Component } from '@vanilla-bean/components';

const button = new Component({
	tag: 'button',
	textContent: 'Click me',
	className: 'primary-btn',
	onPointerPress: () => alert('Hello!'),
	appendTo: document.body,
});

Reactive State Management

Oxject is included in VBC. Import it alongside your components:

import { Component, Oxject } from '@vanilla-bean/components';

const user = new Oxject({ name: 'Alice', online: false });

new Component({
	tag: 'div',
	textContent: user.subscriber('name', name => `Welcome, ${name}!`),
	className: user.subscriber('online', online => (online ? 'user-online' : 'user-offline')),
	appendTo: document.body,
});

// UI updates automatically
user.name = 'Bob';
user.online = true;

Styled Components with Theme Integration

Create scoped, themed components:

import { Component, styled } from '@vanilla-bean/components';

const Card = styled(
	Component,
	({ colors }) => `
	background: ${colors.white};
	border-radius: 8px;
	box-shadow: 0 2px 8px ${colors.alpha(colors.black, 0.1)};
	padding: 24px;

	&:hover {
		transform: translateY(-2px);
		transition: transform 0.2s ease;
	}
`,
);

new Card({
	append: [
		new Component({ tag: 'h3', textContent: 'Card Title' }),
		new Component({ tag: 'p', textContent: 'Card content goes here.' }),
	],
	appendTo: document.body,
});

Complete Reactive Application

import { Component, Oxject, styled } from '@vanilla-bean/components';

// Reactive state
const state = new Oxject({ count: 0 });

// Styled component
const Counter = styled(
	Component,
	({ colors }) => `
	display: flex;
	gap: 12px;
	padding: 16px;
	background: ${colors.dark(colors.gray)};
	border-radius: 6px;
`,
);

// Component with reactive options
new Counter({
	textContent: state.subscriber('count', count => `Count: ${count}`),
	append: [
		new Component({
			tag: 'button',
			textContent: 'Increment',
			onPointerPress: () => state.count++,
		}),
		new Component({
			tag: 'button',
			textContent: 'Reset',
			onPointerPress: () => (state.count = 0),
		}),
	],
	appendTo: document.body,
});

Core Architecture

Options-First Design Philosophy

Components configure through reactive options rather than imperative method calls:

// ❌ Traditional DOM manipulation
const elem = createElement('div');
elem.classList.add('container');
elem.style.padding = '20px';
elem.textContent = 'Hello';
elem.addEventListener('click', handler);
document.body.appendChild(elem);

// ✅ Options-first approach
new Component({
	tag: 'div',
	addClass: 'container',
	style: { padding: '20px' },
	textContent: 'Hello',
	onPointerPress: handler,
	appendTo: document.body,
});

Core Modules

| Module | Purpose | Key Features | | --- | --- | --- | | Component | Enhanced DOM elements | Lifecycle management, reactive options, automatic cleanup | | Oxject | Reactive state | Proxy-based reactivity, subscriber system, derive() for computed values | | styled | Scoped CSS | Theme integration, native CSS nesting, unique class generation | | Theme | Design system | Color manipulation, font management, accessibility helpers |

All four are available from '@vanilla-bean/components' directly. For HTTP caching, hypertether is a separate install (bun add @vanilla-bean/hypertether).

Component Library

25 pre-built components handle common UI patterns:

Forms & Input

  • Input - All HTML input types with validation
  • Select - Dropdown selections with search
  • RadioButton - Radio group input
  • Form - Form management with validation
  • Label - Associated labels with accessibility

Layout & Structure

  • Page - Full-page layouts with header/footer
  • Router - Client-side navigation
  • List - Dynamic lists with filtering
  • Table - Data tables with sorting

Interactive Elements

  • Button - Action buttons with states
  • BottomSheet - Mobile bottom sheet with drag-to-close gesture
  • Dialog - Modal dialogs with backdrop
  • Menu - Context and dropdown menus
  • Notify - Toast-style notifications
  • Popover - Floating content containers
  • Tooltip / TooltipWrapper - Contextual help and information

Specialized Components

  • Calendar - Date selection with events
  • ColorPicker - Color selection interface
  • Keyboard - Virtual keyboard input
  • Whiteboard - Drawing and annotation canvas

Content & Display

  • Code - Syntax-highlighted code blocks
  • Icon - Icon system with Font Awesome integration
  • Link - Enhanced anchor elements
  • TagList - Tag management and display

Real-World Example

Complete user management interface with data loading and state management:

import { Component, Oxject, styled } from '@vanilla-bean/components';
import { GET } from '@vanilla-bean/hypertether';

// Application state
const app = new Oxject({
	users: [],
	loading: false,
	selectedUserId: null,
});

// Styled components
const UserCard = styled(
	Component,
	({ colors }) => `
	padding: 16px;
	border: 1px solid ${colors.light(colors.gray)};
	border-radius: 6px;
	cursor: pointer;

	&:hover { background: ${colors.lightest(colors.blue)}; }
	&.selected { border-color: ${colors.blue}; }
`,
);

const LoadingSpinner = styled(
	Component,
	() => `
	display: inline-block;
	animation: spin 1s linear infinite;
	@keyframes spin { to { transform: rotate(360deg); } }
`,
);

// Data loading with automatic UI updates
async function loadUsers() {
	app.loading = true;

	const { body: users } = await GET('/api/users', {
		onResponse: ({ body }) => (app.users = body), // Auto-refresh on cache invalidation
	});

	app.users = users;
	app.loading = false;
}

// User list with conditional rendering
new Component({
	className: 'user-list',
	content: app.subscriber('loading', loading =>
		loading
			? new LoadingSpinner({ textContent: 'Loading...' })
			: app.subscriber('users', users =>
					users.map(
						user =>
							new UserCard({
								textContent: user.name,
								className: app.subscriber('selectedUserId', selectedId => (selectedId === user.id ? 'selected' : '')),
								onPointerPress: () => (app.selectedUserId = user.id),
							}),
					),
				),
	),
	appendTo: document.body,
});

loadUsers();

Browser Compatibility

Evergreen browsers only. VBC uses native CSS nesting and requires no polyfills or transpilation:

| Feature | Chrome/Edge | Firefox | Safari | | --------------------------- | ----------- | ------- | ------ | | ES6 classes, modules, Proxy | 49+ | 18+ | 10+ | | Native CSS nesting | 112+ | 117+ | 16.5+ |

Effective minimum: Chrome/Edge 112, Firefox 117, Safari 16.5 (all released in 2023).

If you need older browser support, VBC is not the right tool. The native CSS nesting requirement is not negotiable without reintroducing a build-time CSS processing step.

Development

Local Development

# Clone and install
git clone https://github.com/fatlard1993/vanilla-bean-components
cd vanilla-bean-components
bun install

# Start interactive explorer
bun start        # Component explorer at http://localhost:9999

# Run tests
bun test         # Full test suite
bun test:watch   # Watch mode for development

# Build and validation
bun run build    # Clean build with component indexing
bun run lint     # ESLint validation
bun run format   # Code formatting with Prettier

Project Structure

vanilla-bean-components/
├── components/         # 25 pre-built UI components
├── Component/          # Core Component class
├── styled/             # Scoped styling system
├── theme/              # Design tokens and helpers
├── demo/               # Component explorer - live option editor, API docs, example apps
└── docs/               # Additional documentation

Creating Components

Generate new components with full scaffolding:

bun run create:component MyComponent

This creates implementation, demo, tests, and documentation files following project patterns.

Contributing

  1. Fork and clone the repository
  2. Create feature branch from main
  3. Follow existing patterns - Check similar components for code style
  4. Add comprehensive tests - Use @testing-library/dom patterns
  5. Update documentation - Include README updates and demos
  6. Run validation - bun run lint && bun test before submitting

See individual component READMEs for architecture patterns and implementation guidelines.

License

MIT License - see LICENSE file for details.


Browse ComponentsAPI DocumentationLive DemoGetting Started GuideDesign Philosophy