Package Exports
- @jasonshimmy/custom-elements-runtime
- @jasonshimmy/custom-elements-runtime/runtime.d.ts
Readme
Custom Elements Runtime
Ultra-lightweight, type-safe runtime for fast, reactive, and maintainable web components.
Build modern web components with strict TypeScript, zero dependencies, and a functional API. Designed for performance, standards compliance, and developer productivity.
✨ Features
- Reactive State: Automatic re-renders using ES6 Proxy; direct assignment supported. State changes trigger batched updates for performance.
- Attribute-State Sync: All primitive state keys (string, number, boolean) are automatically observed as attributes and kept in sync. Parent-to-child communication is seamless.
- Functional Templates: Use plain functions, tagged helpers (
html,compile), or async Promises. Templates can be compiled for performance and SSR. - Refs: Direct DOM access via
refsfor imperative logic and event handling. No complex selectors required. - Computed Properties: Define derived, reactive values with
computedfor efficient state management. - Automatic Event Binding: Declarative, type-safe handlers via
data-on-*attributes. Only one handler per event type per element is attached; previous handlers are removed on rerender. Handlers are defined directly on the component config. - Controlled Input Sync: Inputs with
data-model(including checkboxes, radios, multi-checkbox groups, and modifiers) stay in sync with state. User typing always wins. VDOM patching ensures reliable event handling and focus preservation. - Deep State Binding: Use
data-bindfor two-way binding to nested or dynamic state (objects, arrays, lists). Supports dot notation and array indices for deep binding. - Global Store: Use the built-in
Storeclass for global, reactive state management across components. Subscribe to changes and update state directly. - Global Event Bus: Use the built-in
eventBusfor cross-component communication. Emit, listen, and unsubscribe from global events with event storm protection. - SSR & Hydration: Universal rendering and opt-in hydration. Templates must match for hydration. SSR excludes refs, event listeners, and lifecycle hooks. Hydration is opt-in via the
hydrateproperty. - Error Boundaries: Optional
onErrorfor fallback UI and diagnostics. Robust error handling and recovery for all lifecycle and render errors. - Focus Preservation: Inputs retain focus and selection during updates, even with rapid state changes.
- Smart DOM Batching: State-triggered renders are batched using requestAnimationFrame for optimal performance.
- Plugin System: Extend runtime behavior with hooks (
onInit,onRender,onError). Plugins can be registered globally and affect all components. - Debug Mode: Enable detailed runtime logs for any component via
debug: truein the config. Logs warnings, errors, and mutation diagnostics. - Strict TypeScript: Type-safe, developer-friendly, zero dependencies. All APIs and configuration are strictly typed.
- Tree-shakable & Modular: Import only what you use. All exports are modular and optimized for tree-shaking.
- Functional API: No classes, no boilerplate. All configuration is functional and declarative.
- Template Helpers: Use
html,compile,css,classes,styles,ref, andonfor efficient, type-safe template authoring. - Build Tools: Integrate with Vite, Webpack, or Rollup for build-time template compilation and optimization.
- VDOM Utilities: Fine-grained DOM diffing and patching for controlled inputs, event listeners, and efficient updates.
Limitations & Edge Cases
- Templates must have a single root node. Fragment templates are supported, but reconciliation is strict and positional; use keys for robust updates.
- Only one event handler per event type per element is attached; previous handlers are removed on rerender. Handlers must be defined on the config object.
- Controlled input sync prioritizes user typing (focused/dirty inputs) over state updates. VDOM patching is regression-tested for reliability.
- SSR hydration is opt-in via the
hydrateproperty. If no region is marked, the entire shadow root is hydrated. SSR excludes refs, event listeners, and lifecycle hooks. - All user-generated content is escaped in templates using
htmlandcompilehelpers. Static HTML is not escaped. - Only features documented here and in
src/lib/runtime.tsare supported. Undocumented features may not work as expected. - VDOM patching for controlled inputs (checkboxes, radios, etc.) is regression-tested to ensure event listeners are always attached and state updates are reliable.
🚀 Getting Started
Install
npm install @jasonshimmy/custom-elements-runtimeImport and use in your project
import { component, html } from '@jasonshimmy/custom-elements-runtime';
component('my-counter', {
state: { count: 0 },
template: ({ count }) => html`
<button data-on-click="increment">Count: ${count}</button>
`({ count }),
increment(_e, state) { state.count++; }
});TypeScript support
All types and interfaces are available for auto-completion and strict typing in code editors. No extra configuration is needed.
Advanced usage
See the API Reference for advanced configuration, SSR, plugin system, global store, event bus, and more.
🎯 Use Cases
- Micro-frontends: Lightweight, isolated components
- Progressive Enhancement: Add reactivity to existing sites
- Design Systems: Reusable component libraries
- SSR Applications: Universal rendering with hydration
- Performance-Critical Apps: When bundle size matters
- Web Standards: Future-proof, standards-based development
⚠️ SSR Caveats
- SSR only generates HTML and styles; DOM APIs, refs, and event listeners are not available during server rendering.
- Lifecycle hooks (
onMounted,onUnmounted) and refs are ignored during SSR. - Hydration requires the client bundle to match the server-rendered markup and state exactly.
🛡️ Production-Readiness
- Strict TypeScript, modular structure
- Early returns, guard clauses, custom error types
- No external dependencies
- Manual input validation and error handling
⚡ Performance Features
- Batched Updates: Multiple state changes are batched using RAF
- Template & Computed Property Caching: Expensive calculations are cached
- Memory Management: Automatic cleanup prevents memory leaks
- Focus Preservation: Smart input focus handling during updates
- Fine-grained DOM diffing: Only changed DOM nodes are updated, not replaced, for optimal performance and UX
- Async rendering: Supports Promises in templates for async data and UI
- Selective hydration: Hydrate only regions marked with
data-hydratefor efficient SSR
Documentation
- API Reference: All runtime exports, configuration options, and advanced patterns.
- Core Concepts: State, attribute sync, event binding, input binding, global store, event bus, lifecycle, error boundaries, SSR, plugin system.
- Data Model vs Data Bind: Comparison, use cases, modifiers, deep binding, edge cases.
- Advanced Use Cases: Patterns for event bus, store, plugin system, async templates, error boundaries, SSR, VDOM.
- Form Input Bindings: All supported input types, modifiers, deep binding, edge cases, VDOM patching.
- SSR Guide: SSR, hydration, limitations, API.
- Framework Comparison: Unique features, tradeoffs, strengths, and when to choose each approach.
- Framework Integration: Using with React, Vue, Angular, Svelte, and Lit.
- Examples: Concise, accurate code for all features and patterns.
See the API Reference for detailed usage, configuration options, and advanced patterns. For advanced topics, see the linked docs above.
Local development
- Clone this repository
- Run the examples:
npm run dev