DOM Helpers Reactive Library
Fine-grained reactivity for vanilla JavaScript - No framework, no build tools, just JavaScript
Quick Start (30 seconds)
<!DOCTYPE html>
<html>
<head>
<title>Reactive Counter</title>
</head>
<body>
<div id="display"></div>
<button id="btn">Click me</button>
<!-- Just add the script tags - that's it! -->
<script src="01_dh-reactive.min.js"></script>
<script src="07_dh-reactiveUtils-shortcut.js"></script>
<script>
// Create reactive state - pure JavaScript!
const counter = state({ count: 0 });
// Auto-update DOM when state changes
effect(() => {
document.getElementById('display').textContent = counter.count;
});
// Regular event listener - no special syntax
document.getElementById('btn').onclick = () => {
counter.count++; // Just change the value - DOM updates automatically! ✨
};
</script>
</body>
</html>That's it! No npm, no build tools, no compilation, no special syntax. Just pure HTML and JavaScript that works immediately.
What is DOM Helpers Reactive?
DOM Helpers Reactive is a lightweight reactive state management library that brings fine-grained reactivity to vanilla JavaScript that updates the real DOM directly without any of the complexity.
Simply put: It makes your JavaScript objects smart. When you change your data, everything that depends on it updates automatically - efficiently and precisely.
// Regular JavaScript - nothing happens
const user = { name: 'Alice' };
user.name = 'Bob'; // ❌ Just data changed, nothing updates
// With DOM Helpers Reactive - automatic updates
const user = state({ name: 'Alice' });
user.name = 'Bob'; // ✨ All UI showing the name updates instantly!What makes it special:
🎯 Fine-Grained Control - Updates only what changed, not the entire page
🚫 No Virtual DOM - Direct, efficient DOM updates
🏗️ No Build Step - Drop in with <script> tags and start coding
📄 Pure HTML - No template syntax, no text interpolation like , no special directives
🔧 Pure JavaScript - Uses native JavaScript APIs you already know
🎓 Easy to Learn - If you know JavaScript, you already know 90% of this library
🌍 Universal - For JavaScript devs, React devs, Vue devs - everyone!
DOM Helpers Reactive is a fully standalone library with no dependencies. It can be used on its own to build real-world reactive applications or seamlessly added to existing non-reactive projects to introduce reactivity.
It acts as a lightweight reactivity engine that works anywhere — simply include the script tag, and you can start using it immediately.
What is Direct DOM Reactivity?
Direct DOM Reactivity means the library updates the actual DOM directly when your state changes, without any intermediate layers.
✅ Direct DOM Reactivity (This Library)
State Change → Track which effects depend on it
→ Re-run only those effects
→ Update Real DOM directly
Benefits:
- No Virtual DOM overhead
- No diffing or reconciliation
- Surgical precision - only what changed
- Faster and more efficientVisual Comparison
Virtual DOM Approach:
Your Code
↓
Virtual DOM (Memory)
↓
Diff Algorithm (Compares old vs new)
↓
Patch List (What changed?)
↓
Real DOM (Finally!)
⏱️ Multiple steps, more workDirect DOM Approach:
Your Code
↓
Dependency Tracking (Automatic)
↓
Real DOM (Directly!)
⚡ Immediate, efficientWho Is This For?
DOM Helpers does more than just make your code reactive — it helps you improve and level up your JavaScript skills. It is JavaScript built on top of native browser APIs, with no heavy abstractions that pull you away from the language itself.
When you learn DOM Helpers Reactive, you’re not learning a new programming language or mindset. You’re going deeper into pure JavaScript. This is what naturally strengthens your JavaScript fundamentals.
The best part is that everything is visible and understandable. You know exactly when and how the DOM is updated, which makes the learning process both engaging and genuinely enjoyable.
✅ Perfect for JavaScript Developers
If you know basic JavaScript, you can use this library. That's it.
No need to learn:
- JSX or template syntax
- Virtual DOM concepts
- Build tools (webpack, vite, etc.)
- Package managers (npm, yarn)
- Compilation or transpilation
- Special component models
- Framework-specific patterns
You already know everything you need:
- JavaScript objects and functions
- DOM APIs like
document.getElementById() - Event listeners like
onclick - Array methods like
map()andfilter()
✅ Perfect for React Developers
Coming from React? You'll feel right at home:
// React hooks-like pattern
const count = ref(0);
effect(() => {
console.log(count.value); // Like useEffect
});
// Computed values
const doubled = computed(() => count.value * 2); // Like useMemoBut simpler - no rules of hooks, no re-rendering, no reconciliation.
✅ Perfect for Vue Developers
Coming from Vue? This will feel familiar:
// Vue-like reactive state
const state = reactive({
count: 0,
doubled() { return this.count * 2; } // Like computed
});
watch(state, 'count', (newVal, oldVal) => {
console.log('Changed!'); // Like watch
});But lighter - no compiler, no template parsing, no build step.
✅ Perfect for jQuery Developers
Used to jQuery? This is a natural evolution:
// jQuery way - manual updates
$('#btn').click(() => {
count++;
$('#display').text(count); // Manual DOM update
});
// Reactive way - automatic updates
effect(() => {
$('#display').text(state.count); // Auto-updates when state changes
});
$('#btn').click(() => {
state.count++; // Just change state - jQuery DOM updates automatically!
});Works alongside jQuery - not instead of it. Add reactivity to your existing jQuery projects!
Whether you’re building a plain JavaScript project, working with an existing codebase, or even maintaining a jQuery-based application, you can easily add reactivity using this reactive engine.
Everything is possible with this approach. Using it is simple — just wrap your UI rendering logic inside an effect() function, and that’s it.
✅ Perfect for Beginners
Learning JavaScript? This library will actually help you learn and improve:
- Uses native JavaScript APIs - you'll learn real DOM manipulation
- No magic - you can read and understand the code
- Teaches reactive programming concepts
- Makes JavaScript more enjoyable and delightful
- Build real projects faster - more motivation to keep learning!
Why Choose DOM Helpers Reactive?
🚀 Zero Setup, Instant Results
Most frameworks:
npm create my-app
cd my-app
npm install
npm run dev
# Wait for build...
# Finally code after 5 minutesDOM Helpers Reactive:
<script src="01_dh-reactive.min.js"></script>
<!-- Start coding immediately! -->🎯 Fine-Grained Reactivity
Other approaches:
- Virtual DOM frameworks - Re-render entire components even for tiny changes
- Manual updates - You track and update everything yourself
DOM Helpers Reactive:
const state = { user: 'Alice', posts: [...100 posts], theme: 'dark' };
state.theme = 'light'; // ✅ Only updates theme-related DOM elements
// ✅ Doesn't touch user or posts
// ✅ No diffing, no reconciliation, no wasted work📄 HTML Stays Pure HTML
Template frameworks require special syntax:
<!-- Vue/Angular/Svelte - special syntax -->
<div>{{ message }}</div>
<div v-if="show">Content</div>
<div *ngIf="show">Content</div>
<div {#if show}>Content</div>
<!-- React - JSX (not HTML) -->
<div>{message}</div>
<div>{show && <span>Content</span>}</div>DOM Helpers Reactive - pure HTML:
<!-- Just regular HTML - no special syntax! -->
<div id="message"></div>
<div id="content"></div>
<script>
// JavaScript handles the logic - separately and clearly
effect(() => {
document.getElementById('message').textContent = state.message;
document.getElementById('content').style.display = state.show ? 'block' : 'none';
});
</script>Benefits: ✅ Works with any HTML editor
✅ No build step to compile templates
✅ HTML is HTML, JavaScript is JavaScript
✅ Clear separation of concerns
✅ Easy to debug - inspect in DevTools like normal
🔧 JavaScript Stays JavaScript
You're writing real JavaScript, not a framework-specific dialect:
// Real JavaScript objects
const user = state({ name: 'Alice', age: 25 });
// Real JavaScript methods
state.todos.push({ text: 'Learn reactive' });
state.todos.filter(t => !t.done);
// Real JavaScript conditionals
if (state.count > 10) {
state.status = 'high';
}
// Real JavaScript loops
state.items.forEach(item => console.log(item));
// Real DOM APIs
document.getElementById('app').textContent = state.message;
document.querySelector('.btn').onclick = () => state.count++;No framework magic. No special rules. Just JavaScript with superpowers.
🌐 Add to ANY Existing Project
Unlike frameworks that require rewriting everything, DOM Helpers Reactive drops into any project:
Plain JavaScript project:
<!-- Your existing app -->
<script src="your-app.js"></script>
<!-- Add reactivity -->
<script src="01_dh-reactive.min.js"></script>
<script src="07_dh-reactiveUtils-shortcut.js"></script>
<!-- Now make parts of your app reactive -->
<script>
const reactiveData = state(window.myExistingData);
// Done! Now it's reactive
</script>jQuery project:
<!-- Your existing jQuery -->
<script src="jquery.js"></script>
<script src="your-jquery-code.js"></script>
<!-- Add reactivity -->
<script src="01_dh-reactive.min.js"></script>
<!-- Mix jQuery with reactive state -->
<script>
const state = ReactiveUtils.state({ theme: 'light' });
effect(() => {
$('body').toggleClass('dark', state.theme === 'dark');
});
$('.theme-toggle').click(() => {
state.theme = state.theme === 'light' ? 'dark' : 'light';
});
</script>Bootstrap/Tailwind project:
<!-- Your existing styles -->
<link href="bootstrap.css" rel="stylesheet">
<!-- Add reactivity -->
<script src="01_dh-reactive.min.js"></script>
<!-- Reactive Bootstrap components -->
<script>
const modal = state({ open: false });
effect(() => {
$('#myModal').modal(modal.open ? 'show' : 'hide');
});
</script>Legacy project with no build tools:
<!-- Just drop it in! -->
<script src="01_dh-reactive.min.js"></script>
<!-- That's it - start using it immediately -->🎓 Learn JavaScript, Not Framework APIs
Framework-specific learning:
- React: hooks, JSX, reconciliation, fiber, suspense...
- Vue: template syntax, SFC, composition API, reactivity transform...
- Angular: TypeScript, RxJS, decorators, modules, zones...
DOM Helpers Reactive learning:
- JavaScript objects ✅ (you already know this)
- Functions ✅ (you already know this)
- DOM APIs ✅ (you already know this)
state()- creates reactive objecteffect()- runs code when dependencies change
That's it. Two new concepts. Everything else is JavaScript you already know.
Bonus: Using this library improves your JavaScript skills because you're using native APIs directly, not framework abstractions.
Core Philosophy
DOM Helpers Reactive is built on these principles:
1️⃣ JavaScript First
HTML is for structure. JavaScript is for logic. Keep them separate.
// ✅ Logic in JavaScript (clear and testable)
const state = ReactiveUtils.state({
count: 0,
doubled() { return this.count * 2; }
});
effect(() => {
document.getElementById('count').textContent = state.count;
document.getElementById('doubled').textContent = state.doubled;
});
// ❌ NOT this (mixing logic into HTML)
// <div>{{ count * 2 }}</div>2️⃣ Learn Once, Use Everywhere
Skills you build transfer to:
- Modern vanilla JavaScript projects
- Working with native Web Components
- Understanding how frameworks work under the hood
- Building browser extensions
- Server-side DOM manipulation (JSDOM)
- Any JavaScript environment
3️⃣ Progressive Enhancement
Start simple, add complexity only when needed:
// Simple - just state
const count = state({ value: 0 });
// Add computed when needed
count.computed('doubled', function() { return this.value * 2; });
// Add persistence when needed
autoSave(count, 'myCount', { storage: 'localStorage' });
// Add validation when needed
const form = form({ email: '' }, {
validators: { email: validators.email() }
});Each feature is optional and independent.
4️⃣ Real-World Ready
Not a toy. Build production applications:
✅ Memory management - Automatic cleanup prevents leaks
✅ Error handling - Error boundaries isolate failures
✅ Async support - Built-in race condition prevention
✅ Batching - Efficient update scheduling
✅ Deep reactivity - Nested objects work automatically
✅ Cross-tab sync - State syncs across browser tabs
✅ TypeScript support - Type definitions available (if needed)
✅ Production tested - Used in real applications
Mental Model
The "Smart Home" Analogy
Think of regular JavaScript objects like a dumb house:
Regular Object (Manual Control)
┌─────────────────────────────────────┐
│ │
│ Temperature Dial │
│ [75°F] │ ← You change it
│ │
│ Thermostat Display │
│ [Shows: 65°F] │ ← Doesn't update!
│ │
│ You must manually: │
│ 1. Change dial │
│ 2. Update display │
│ 3. Turn on heater │
│ 4. Adjust fan speed │
│ 5. ...everything yourself │
│ │
└─────────────────────────────────────┘Reactive state is like a smart home:
Reactive State (Automatic Control)
┌─────────────────────────────────────┐
│ │
│ Temperature Dial │
│ [75°F] │ ← You change it
│ │ │
│ ├──→ Display updates │ ✨ Automatic
│ ├──→ Heater turns on │ ✨ Automatic
│ ├──→ Fan adjusts │ ✨ Automatic
│ └──→ App notifies you │ ✨ Automatic
│ │
│ Change one thing, │
│ everything connected updates! │
│ │
└─────────────────────────────────────┘Key Insight: You only change the data you care about. Everything else updates automatically based on those changes.
How Does It Work?
The Magic: JavaScript Proxies
DOM Helpers Reactive uses JavaScript Proxies to intercept property access and track dependencies:
Your Code Proxy System DOM Updates
──────────────────────────────────────────────────────────────────
const state = { ┌─────────────┐
count: 0 │ Proxy wraps │
} │ your object │
└─────────────┘
effect(() => { ┌─────────────┐
display.text = │ Tracks: │
state.count; │ "This effect │
}) │ reads count"│
└─────────────┘
state.count = 5; ┌─────────────┐
│ Detects │ 💡
│ change, │ DOM shows "5"
│ re-runs │
│ effect │
└─────────────┘Step by step:
1️⃣ You create reactive state
const state = ReactiveUtils.state({ count: 0 });
// Proxy wraps the object2️⃣ You read a property in an effect
effect(() => {
console.log(state.count); // 👀 Proxy sees this!
});
// Proxy records: "This effect depends on 'count'"3️⃣ You change the property
state.count = 10; // ✍️ Proxy sees this too!4️⃣ Proxy automatically re-runs effects
// Proxy: "count changed, these effects depend on it"
// → Re-runs effect
// → Console logs: 10The beautiful part: You never have to manually track what depends on what. The Proxy does it all automatically by watching what you read and what you write.
Fine-Grained Updates
Unlike Virtual DOM frameworks that re-render entire components, DOM Helpers Reactive updates only what changed:
const app = state({
user: { name: 'Alice', avatar: 'alice.jpg' },
posts: [...100 posts],
theme: 'light'
});
// Effect 1: Only runs when user.name changes
effect(() => {
document.getElementById('username').textContent = app.user.name;
});
// Effect 2: Only runs when theme changes
effect(() => {
document.body.className = app.theme;
});
// Effect 3: Only runs when posts change
effect(() => {
renderPosts(app.posts);
});
// Change theme - only Effect 2 runs! ✨
app.theme = 'dark';
// Effects 1 and 3 don't run - they don't depend on theme
// No wasted work, no unnecessary updatesThis is fine-grained reactivity - surgical precision, no collateral updates.
Complete Feature Set
🎯 Core Reactivity
Reactive State
const user = state({ name: 'Alice', age: 25 });
user.name = 'Bob'; // ✨ All dependent effects run automaticallyEffects - Auto-run code when dependencies change
effect(() => {
document.getElementById('name').textContent = user.name;
}); // Runs now and whenever user.name changesComputed Properties - Derived values that update automatically
user.computed('greeting', function() {
return `Hello, ${this.name}!`;
});
console.log(user.greeting); // "Hello, Bob!" (auto-updates)Watchers - React to specific property changes
watch(user, 'age', (newAge, oldAge) => {
console.log(`Age changed from ${oldAge} to ${newAge}`);
});Batching - Group multiple updates for efficiency
batch(() => {
user.name = 'Charlie';
user.age = 30;
user.email = 'charlie@example.com';
}); // All effects run once, not three timesDeep Reactivity - Nested objects are automatically reactive
const app = state({
user: { profile: { name: 'Alice' } }
});
app.user.profile.name = 'Bob'; // ✨ Works! Deep reactivity🎨 DOM Integration
Declarative Bindings
// Bind state to DOM elements
const counter = state({ count: 0 });
bindings({
'#display': () => counter.count,
'#doubled': () => counter.count * 2,
'.status': {
className: () => counter.count > 10 ? 'high' : 'low',
textContent: () => `Count: ${counter.count}`
}
});Direct DOM Updates
// Or use effects for custom logic
effect(() => {
const display = document.getElementById('display');
display.textContent = counter.count;
display.style.color = counter.count > 10 ? 'red' : 'black';
});Mixed Updates - Update state and DOM together
updateAll(state, {
count: 10, // State update
'#display': { textContent: '10' } // DOM update
});📦 Data Structures
Refs - Simple reactive values
const count = ref(0);
count.value++; // Access via .value
const { name, age } = refs({ name: 'Alice', age: 25 });
name.value = 'Bob';Collections - Reactive arrays with rich API
const todos = collection([
{ id: 1, text: 'Learn reactive', done: false }
]);
todos.add({ id: 2, text: 'Build app', done: false });
todos.remove(t => t.id === 1);
todos.update(t => t.id === 2, { done: true });
todos.toggle(t => t.id === 2); // Toggle done field
todos.filter(t => !t.done);
todos.map(t => t.text);
todos.clear();
console.log(todos.length); // Reactive property
console.log(todos.first, todos.last);Forms - Complete form state management
const loginForm = form(
{ email: '', password: '' },
{
validators: {
email: validators.combine(
validators.required('Email required'),
validators.email('Invalid email')
),
password: validators.minLength(8, 'Password too short')
},
async onSubmit(values) {
await api.login(values);
}
}
);
// Rich form API
loginForm.setValue('email', 'user@example.com');
loginForm.validateField('email');
loginForm.setError('email', 'Email already exists');
loginForm.touchAll();
console.log(loginForm.isValid);
console.log(loginForm.errors.email);
console.log(loginForm.touched.email);
await loginForm.submit();Async State - Loading states, error handling, race condition prevention
const userData = asyncState(null, {
onSuccess: (data) => console.log('Loaded!', data),
onError: (error) => console.error('Failed!', error)
});
// Execute with automatic cancellation
await execute(userData, async (signal) => {
const response = await fetch('/api/user', { signal });
return response.json();
});
console.log(userData.loading); // false
console.log(userData.data); // { name: 'Alice', ... }
console.log(userData.error); // null
console.log(userData.isSuccess); // true
// Abort current request
abort(userData);
// Refetch
await refetch(userData);
// Reset to initial state
reset(userData);🏪 Store Pattern
Vuex/Redux-like Stores
const store = store(
// State
{ count: 0, user: null },
{
// Computed (like getters)
getters: {
doubled() { return this.count * 2; },
isLoggedIn() { return this.user !== null; }
},
// Actions
actions: {
increment(state, amount = 1) {
state.count += amount;
},
async login(state, credentials) {
state.user = await api.login(credentials);
}
}
}
);
// Usage
console.log(store.doubled); // Computed
store.increment(5); // Action
await store.login({ email: '...', password: '...' });🧩 Component Pattern
Encapsulated Components
const counter = component({
// State
state: { count: 0 },
// Computed
computed: {
doubled() { return this.count * 2; }
},
// Watch for changes
watch: {
count(newVal, oldVal) {
console.log(`Count changed to ${newVal}`);
}
},
// Effects
effects: {
updateDOM() {
document.getElementById('count').textContent = this.count;
}
},
// Actions
actions: {
increment(state) {
state.count++;
}
},
// Lifecycle
mounted() {
console.log('Component mounted');
},
unmounted() {
console.log('Component destroyed');
}
});
// Usage
counter.increment();
destroy(counter); // Cleanup💾 Storage & Persistence
Auto-Save to localStorage/sessionStorage
const preferences = state({ theme: 'light', language: 'en' });
// Automatically save to localStorage
autoSave(preferences, 'userPreferences', {
storage: 'localStorage',
debounce: 300, // Wait 300ms before saving
autoLoad: true, // Load on initialization
onSave: (data) => {
console.log('Saved!', data);
return data; // Transform before saving if needed
}
});
// Manual operations
save(preferences); // Force save now
load(preferences); // Reload from storage
clear(preferences); // Remove from storage
exists(preferences); // Check if exists
stopAutoSave(preferences); // Pause auto-saving
startAutoSave(preferences); // Resume auto-saving
// Get storage info
const info = storageInfo(preferences);
console.log(info.sizeKB); // Size in KBCross-Tab Synchronization
const sharedState = state({ message: 'Hello' });
autoSave(sharedState, 'sharedMessage', {
storage: 'localStorage',
sync: true, // ✨ Sync across tabs!
onSync: (newData) => {
console.log('Synced from another tab!', newData);
}
});
// Change in one tab - updates in all tabs automatically!Reactive Storage
// Create reactive storage proxy
const storage = reactiveStorage('localStorage', 'myApp');
// Use like a reactive object
effect(() => {
console.log(storage.get('theme')); // Re-runs when 'theme' changes
});
storage.set('theme', 'dark'); // Effect re-runs!
storage.remove('theme');
console.log(storage.has('theme'));
console.log(storage.keys()); // All keysWatch Storage Keys
// Watch specific key changes
watchStorage('theme', (newVal, oldVal) => {
console.log(`Theme changed from ${oldVal} to ${newVal}`);
}, {
storage: 'localStorage',
immediate: true // Run immediately with current value
});🛡️ Production Features
Memory Management - Automatic cleanup
const state = ReactiveUtils.state({ count: 0 });
const cleanup1 = effect(() => console.log(state.count));
const cleanup2 = watch(state, 'count', () => { /* ... */ });
// Clean up individual effects
cleanup1();
cleanup2();
// Or clean up everything at once
cleanup(state);Cleanup Collectors
const collector = collector();
collector.add(effect(() => { /* ... */ }));
collector.add(watch(state, 'count', () => { /* ... */ }));
collector.add(() => console.log('Custom cleanup'));
// Clean up everything
collector.cleanup();Cleanup Scopes
const cleanup = scope((collect) => {
// All effects are automatically collected
collect(effect(() => { /* ... */ }));
collect(watch(state, 'key', () => { /* ... */ }));
});
// One call cleans up everything
cleanup();Error Boundaries
// Safe effects with error handling
safeEffect(() => {
riskyOperation();
}, {
errorBoundary: {
onError: (error, context) => {
console.error('Effect error:', error);
logToErrorService(error);
},
fallback: (error) => 'Fallback value',
retry: true,
maxRetries: 3,
retryDelay: 1000
}
});Async Effects with Cancellation
// Automatically cancels when dependencies change
asyncEffect(async (signal) => {
const response = await fetch('/api/data', { signal });
const data = await response.json();
// This only runs if not cancelled
state.data = data;
}, {
onError: (error) => {
if (error.name !== 'AbortError') {
console.error('Error:', error);
}
}
});DevTools
// Enable development tools
DevTools.enable();
// Track state for debugging
DevTools.trackState(myState, 'MyState');
// View all states
console.log(DevTools.getStates());
// View change history
console.log(DevTools.getHistory());
// Access globally
window.__REACTIVE_DEVTOOLS__🔧 Array Reactivity
Reactive Array Methods
const state = ReactiveUtils.state({ items: [1, 2, 3] });
// These all trigger reactivity ✨
state.items.push(4);
state.items.pop();
state.items.shift();
state.items.unshift(0);
state.items.splice(1, 1);
state.items.sort();
state.items.reverse();
// Manual patching if needed
patchArray(state, 'items');🎛️ Advanced Control
Pause/Resume Reactivity
pause(); // Pause all updates
state.a = 1;
state.b = 2;
state.c = 3; // No effects run yet
resume(true); // Resume and flush updates (effects run once)Untrack - Run code without tracking dependencies
effect(() => {
console.log(state.count); // Tracked
untrack(() => {
console.log(state.other); // NOT tracked
});
});Manual Notify
state.count = 5;
notify(state, 'count'); // Manually trigger effectsArchitecture Overview
DOM Helpers Reactive uses a modular architecture - use only what you need:
📦 Modular System
├─→ 01_dh-reactive.js (8KB) - CORE
│ ├─ Reactive state (state, ref, refs)
│ ├─ Effects & watchers
│ ├─ Computed properties
│ ├─ Batching system
│ ├─ DOM bindings
│ ├─ Collections, forms, async state
│ ├─ Store & component patterns
│ └─ Reactive builder
│
├─→ 02_dh-reactive-array-patch.js (2KB)
│ └─ Makes array methods reactive
│
├─→ 03_dh-reactive-collections.js (3KB)
│ └─ Rich collection API (40+ methods)
│
├─→ 04_dh-reactive-form.js (4KB)
│ └─ Form management & validation
│
├─→ 05_dh-reactive-cleanup.js (3KB)
│ └─ Memory leak prevention & cleanup
│
├─→ 06_dh-reactive-enhancements.js (5KB)
│ ├─ Error boundaries
│ ├─ Async effects with cancellation
│ ├─ Safe effects with retry
│ └─ DevTools integration
│
├─→ 07_dh-reactiveUtils-shortcut.js (2KB)
│ └─ Global shortcuts (no namespace)
│
├─→ 08_dh-reactive-storage.js (4KB)
│ ├─ Auto-save (localStorage/sessionStorage)
│ ├─ Cross-tab sync
│ └─ Reactive storage API
│
└─→ 09_dh-reactive-namespace-methods.js (2KB)
└─ Alternative API stylesTotal Size: ~33KB for everything (10KB gzipped)
Core Only: 8KB (3KB gzipped)
Loading Strategies
Loading Complete Library
<script src="dh-reactive.min.js"></script>Minimal (Just reactivity - 8KB):
<script src="01_dh-reactive.min.js"></script>Recommended (Core + shortcuts + cleanup - 13KB):
<script src="01_dh-reactive.min.js"></script>
<script src="05_dh-reactive-cleanup.min.js"></script>
<script src="07_dh-reactiveUtils-shortcut.js"></script>Full-Featured (Everything - 33KB):
<script src="01_dh-reactive.min.js"></script>
<script src="02_dh-reactive-array-patch.min.js"></script>
<script src="03_dh-reactive-collection.min.js"></script>
<script src="04_dh-reactive-form.min.js"></script>
<script src="05_dh-reactive-cleanup.min.js"></script>
<script src="06_dh-reactive-enhancements.min.js"></script>
<script src="07_dh-reactive-storage.min.js"></script>
<script src="08_dh-reactive-namespace-methods.min.js"></script>
<script src="09_dh-reactiveUtils-shortcut.min.js"></script>Pick and Choose:
<!-- Only need forms? -->
<script src="01_dh-reactive.min.js"></script>
<script src="04_dh-reactive-form.min.js"></script>
<!-- Only need storage? -->
<script src="01_dh-reactive.min.js"></script>
<script src="08_dh-reactive-storage.js"></script>
<!-- Building List? -->
<script src="01_dh-reactive.min.js"></script>
<script src="02_dh-reactive-array-patch.min.js"></script>
<script src="03_dh-reactive-collections.js"></script>How To Use It
Installation
No installation needed! Just download and include:
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<div id="app"></div>
<!-- Add the library -->
<script src="path/to/01_dh-reactive.js"></script>
<script src="path/to/07_dh-reactiveUtils-shortcut.js"></script>
<!-- Your code -->
<script>
const app = state({ message: 'Hello World!' });
effect(() => {
document.getElementById('app').textContent = app.message;
});
</script>
</body>
</html>Your First Reactive App
Todo List in Pure HTML + JavaScript:
<!DOCTYPE html>
<html>
<head>
<title>Reactive Todo List</title>
<style>
.done { text-decoration: line-through; color: #888; }
button { margin: 5px; }
</style>
</head>
<body>
<div id="app">
<h1>My Todos</h1>
<input type="text" id="todoInput" placeholder="New todo...">
<button id="addBtn">Add</button>
<div>
<button id="filterAll">All</button>
<button id="filterActive">Active</button>
<button id="filterDone">Done</button>
</div>
<ul id="todoList"></ul>
<div id="stats"></div>
</div>
<!-- Load library -->
<script src="01_dh-reactive.min.js"></script>
<script src="03_dh-reactive-collections.js"></script>
<script src="07_dh-reactiveUtils-shortcut.js"></script>
<script>
// 1️⃣ Create reactive state
const app = state({
todos: collection([]),
filter: 'all',
newTodo: ''
});
// 2️⃣ Add computed properties
app.computed('filteredTodos', function() {
if (this.filter === 'active') {
return this.todos.items.filter(t => !t.done);
}
if (this.filter === 'done') {
return this.todos.items.filter(t => t.done);
}
return this.todos.items;
});
app.computed('stats', function() {
const total = this.todos.length;
const done = this.todos.items.filter(t => t.done).length;
const active = total - done;
return { total, done, active };
});
// 3️⃣ Set up automatic DOM updates
effect(() => {
// Render todos
const html = app.filteredTodos.map(todo => `
<li>
<input type="checkbox"
${todo.done ? 'checked' : ''}
onchange="toggleTodo(${todo.id})">
<span class="${todo.done ? 'done' : ''}">${todo.text}</span>
<button onclick="removeTodo(${todo.id})">Delete</button>
</li>
`).join('');
document.getElementById('todoList').innerHTML = html;
});
effect(() => {
// Render stats
const { total, done, active } = app.stats;
document.getElementById('stats').textContent =
`Total: ${total} | Active: ${active} | Done: ${done}`;
});
// 4️⃣ Create actions (pure JavaScript functions)
function addTodo() {
const text = document.getElementById('todoInput').value.trim();
if (text) {
app.todos.add({
id: Date.now(),
text: text,
done: false
});
document.getElementById('todoInput').value = '';
}
}
function toggleTodo(id) {
app.todos.toggle(t => t.id === id, 'done');
}
function removeTodo(id) {
app.todos.remove(t => t.id === id);
}
function setFilter(filter) {
app.filter = filter;
}
// 5️⃣ Set up event listeners (regular JavaScript)
document.getElementById('addBtn').onclick = addTodo;
document.getElementById('todoInput').onkeypress = (e) => {
if (e.key === 'Enter') addTodo();
};
document.getElementById('filterAll').onclick = () => setFilter('all');
document.getElementById('filterActive').onclick = () => setFilter('active');
document.getElementById('filterDone').onclick = () => setFilter('done');
// ✨ That's it! Just change state and everything updates automatically
</script>
</body>
</html>What just happened:
- Created reactive state with
state()andcollection() - Added computed properties that auto-update
- Set up effects that auto-update the DOM
- Wrote regular JavaScript functions
- Used regular event listeners
No build tools. No compilation. No special syntax. Just JavaScript!
Adding to Existing Projects
Plain JavaScript Project
<!-- Your existing app -->
<script src="your-existing-code.js"></script>
<!-- Add reactivity -->
<script src="01_dh-reactive.min.js"></script>
<script src="07_dh-reactiveUtils-shortcut.js"></script>
<script>
// Make existing data reactive
const reactiveData = state(window.existingData);
// Add effects
effect(() => {
// Now updates automatically when reactiveData changes
updateUI(reactiveData.value);
});
</script>jQuery Project
<!-- Your existing jQuery -->
<script src="jquery-3.6.0.min.js"></script>
<script src="your-jquery-code.js"></script>
<!-- Add reactivity -->
<script src="01_dh-reactive.min.js"></script>
<script>
// Mix jQuery with reactive state
const state = ReactiveUtils.state({
theme: 'light',
count: 0
});
// Reactive jQuery
effect(() => {
$('body').toggleClass('dark-theme', state.theme === 'dark');
$('#counter').text(state.count);
});
// Regular jQuery events
$('.theme-toggle').click(() => {
state.theme = state.theme === 'light' ? 'dark' : 'light';
});
$('.increment').click(() => {
state.count++;
});
</script>Bootstrap Project
<link href="bootstrap.min.css" rel="stylesheet">
<script src="bootstrap.bundle.min.js"></script>
<!-- Add reactivity -->
<script src="01_dh-reactive.min.js"></script>
<script>
const app = state({
showModal: false,
alertMessage: '',
toastMessage: ''
});
// Reactive Bootstrap components
effect(() => {
const modal = new bootstrap.Modal('#myModal');
if (app.showModal) {
modal.show();
} else {
modal.hide();
}
});
effect(() => {
if (app.toastMessage) {
const toast = new bootstrap.Toast('#myToast');
document.querySelector('#myToast .toast-body').textContent = app.toastMessage;
toast.show();
}
});
// Just change state - Bootstrap components react!
app.showModal = true;
app.toastMessage = 'Welcome!';
</script>Legacy Project (No Build Tools)
<!-- Your old project structure -->
<script src="old-library.js"></script>
<script src="legacy-code-1.js"></script>
<script src="legacy-code-2.js"></script>
<!-- Just drop in reactive! -->
<script src="01_dh-reactive.min.js"></script>
<script>
// Gradually migrate parts to reactive
const newFeature = state({ data: [] });
effect(() => {
// Update old code with new reactive data
window.legacyUpdate(newFeature.data);
});
</script>The key: You don't have to rewrite anything. Just add the script tag and start making parts of your app reactive, piece by piece.
What Can You Build?
✅ Web Applications
Todo Apps & Task Managers
const todos = collection([]);
const filter = ref('all');
effect(() => {
const filtered = todos.items.filter(t => {
if (filter.value === 'active') return !t.done;
if (filter.value === 'done') return t.done;
return true;
});
renderTodoList(filtered);
});Dashboards & Data Visualization
const dashboard = state({
metrics: { sales: 0, users: 0, revenue: 0 }
});
// Auto-refresh every 30 seconds
setInterval(async () => {
dashboard.metrics = await fetchMetrics();
}, 30000);
// Charts update automatically
effect(() => {
updateChart(dashboard.metrics);
});E-commerce & Shopping Carts
const cart = collection([]);
cart.computed('total', function() {
return this.items.reduce((sum, item) => sum + item.price * item.qty, 0);
});
cart.computed('itemCount', function() {
return this.items.reduce((sum, item) => sum + item.qty, 0);
});
effect(() => {
document.getElementById('cart-badge').textContent = cart.itemCount;
document.getElementById('cart-total').textContent = `$${cart.total}`;
});Forms & CRUD Apps
const userForm = form(
{ name: '', email: '', password: '' },
{
validators: {
name: validators.required(),
email: validators.combine(
validators.required(),
validators.email()
),
password: validators.minLength(8)
},
async onSubmit(values) {
await api.createUser(values);
showSuccess('User created!');
}
}
);
// Auto-save draft
autoSave(userForm, 'userFormDraft', {
debounce: 1000,
onSave: () => showNotification('Draft saved')
});🎮 Interactive Widgets
Counters, Timers, Calculators
const timer = state({ seconds: 0, running: false });
timer.computed('display', function() {
const mins = Math.floor(this.seconds / 60);
const secs = this.seconds % 60;
return `${mins}:${secs.toString().padStart(2, '0')}`;
});
let interval;
watch(timer, 'running', (running) => {
if (running) {
interval = setInterval(() => timer.seconds++, 1000);
} else {
clearInterval(interval);
}
});Data Tables with Sorting/Filtering
const table = state({
data: [...],
sortBy: 'name',
sortDir: 'asc',
filter: ''
});
table.computed('sortedData', function() {
let result = [...this.data];
if (this.filter) {
result = result.filter(row =>
JSON.stringify(row).toLowerCase().includes(this.filter.toLowerCase())
);
}
result.sort((a, b) => {
const val = a[this.sortBy] > b[this.sortBy] ? 1 : -1;
return this.sortDir === 'asc' ? val : -val;
});
return result;
});
effect(() => {
renderTable(table.sortedData);
});💬 Real-Time Applications
Chat Interfaces
const chat = state({
messages: collection([]),
users: collection([]),
typing: {}
});
// Auto-scroll on new messages
watch(chat.messages, 'length', () => {
scrollToBottom();
});
// WebSocket integration
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'message') {
chat.messages.add(data.message);
}
if (data.type === 'typing') {
chat.typing[data.userId] = true;
}
};Notification Systems
const notifications = collection([]);
notifications.add({
id: Date.now(),
message: 'New message received',
type: 'info',
autoClose: 5000
});
// Auto-remove after timeout
effect(() => {
notifications.items.forEach(notif => {
if (notif.autoClose) {
setTimeout(() => {
notifications.remove(n => n.id === notif.id);
}, notif.autoClose);
}
});
});🔧 Browser Extensions
Chrome/Firefox Extensions
// popup.js
const extension = state({
enabled: true,
settings: { /* ... */ }
});
// Auto-save to chrome.storage
autoSave(extension, 'extensionState', {
storage: 'localStorage', // Use extension storage API instead
onSave: (data) => {
chrome.storage.local.set({ extensionState: data });
},
onLoad: () => {
chrome.storage.local.get(['extensionState'], (result) => {
if (result.extensionState) {
Object.assign(extension, result.extensionState);
}
});
}
});
// Reactive UI
effect(() => {
document.getElementById('toggle').checked = extension.enabled;
});🎨 Creative Projects
Animation Controllers
const animation = state({
playing: false,
progress: 0,
speed: 1
});
effect(() => {
if (animation.playing) {
requestAnimationFrame(() => {
animation.progress += 0.01 * animation.speed;
if (animation.progress >= 1) {
animation.progress = 0;
}
updateAnimation(animation.progress);
});
}
});Game State Management
const game = state({
score: 0,
level: 1,
lives: 3,
paused: false
});
game.computed('gameOver', function() {
return this.lives <= 0;
});
watch(game,'gameOver', (isOver) => {
if (isOver) {
showGameOverScreen();
}
});
watch(game, 'score', (newScore) => {
if (newScore > 1000 && game.level === 1) {
game.level = 2;
showLevelUpAnimation();
}
});Learning & Growth
How This Library Helps You Learn JavaScript
1. You Use Real JavaScript APIs
Unlike frameworks that abstract away the DOM, you use actual browser APIs:
// You learn real DOM manipulation
document.getElementById('app').textContent = state.message;
document.querySelector('.btn').onclick = handleClick;
document.querySelectorAll('.item').forEach(el => { /* ... */ });
// You learn real JavaScript
state.items.map(item => item.name);
state.items.filter(item => item.active);
state.items.reduce((sum, item) => sum + item.value, 0);
// You learn real async JavaScript
async function loadData() {
const response = await fetch('/api/data');
const data = await response.json();
state.data = data;
}Every line of code teaches you transferable JavaScript skills.
2. You Understand How Things Work
Because the library uses JavaScript Proxies (a real JavaScript feature), you learn:
- How proxies intercept object operations
- How dependency tracking works
- How modern frameworks achieve reactivity
- The principles behind Vue 3, Solid.js, and others
3. Skills Transfer Everywhere
What you learn with DOM Helpers Reactive applies to:
- Any JavaScript project - The skills are universal
- Modern frameworks - You understand the concepts they use
- Web Components - Native browser components work the same way
- Node.js - The JavaScript patterns are the same
- Job interviews - Real JavaScript knowledge, not just framework APIs
4. You Build Real Projects
Because there's no steep learning curve, you can:
- Start building immediately
- Finish projects faster
- Stay motivated
- Build a portfolio
- Learn by doing
5. JavaScript Becomes Enjoyable
No more fighting with:
- Complex toolchains
- Cryptic error messages
- Framework-specific gotchas
- Re-learning everything with each framework version
Just write JavaScript, see results, build things. Programming becomes fun again! 🎉
From Beginner to Advanced
Beginner Path:
// Week 1: Basic reactivity
const state = ReactiveUtils.state({ count: 0 });
effect(() => {
document.getElementById('count').textContent = state.count;
});
// Week 2: Collections
const todos = collection([]);
todos.add({ text: 'Learn', done: false });
// Week 3: Computed properties
state.computed('doubled', function() {
return this.count * 2;
});
// Week 4: Forms
const form = form({ email: '' }, {
validators: { email: validators.email() }
});Intermediate Path:
// Month 2: Components
const app = component({
state: { /* ... */ },
computed: { /* ... */ },
actions: { /* ... */ }
});
// Month 3: Async state
const data = asyncState(null);
await execute(data, async (signal) => {
return await fetch('/api', { signal }).then(r => r.json());
});
// Month 4: Storage
autoSave(state, 'myState', {
storage: 'localStorage',
sync: true
});Advanced Path:
// Month 6: Error boundaries
safeEffect(() => {
riskyOperation();
}, { errorBoundary: { /* ... */ } });
// Custom abstractions
function createViewModel(initialData) {
const vm = state(initialData);
vm.computed('isDirty', function() {
return JSON.stringify(this) !== JSON.stringify(initialData);
});
return vm;
}
// Performance optimization
batch(() => {
// Multiple updates
});
pause();
// Batch operations
resume(true);Summary
🎯 What Is DOM Helpers Reactive?
A lightweight, powerful reactive state management library that brings fine-grained reactivity to vanilla JavaScript without any complexity, build tools, or special syntax.
✨ Key Features
For Everyone: ✅ Zero setup - Just add <script> tags and start coding
✅ No build tools - No npm, webpack, or compilation needed
✅ Pure HTML - No template syntax, no text interpolation, no directives
✅ Pure JavaScript - Uses native APIs, no framework-specific patterns
✅ Easy to learn - If you know JavaScript, you're 90% there
✅ Universal - For JS devs, React devs, Vue devs, jQuery devs - everyone
Technical Excellence: 🎯 Fine-grained reactivity - Updates only what changed, nothing more
🚫 No Virtual DOM - Direct, efficient DOM manipulation
🪶 Lightweight - Core is 8KB (3KB gzipped)
🧩 Modular - Use only what you need
💪 Production-ready - Memory management, error handling, async support
🌐 Works everywhere - Plain JS, jQuery, Bootstrap, any existing project
Developer Experience: 🎓 Improves JavaScript skills - Learn real APIs, not framework abstractions
🎉 Makes coding enjoyable - Less boilerplate, more productivity
🚀 Build real projects - From widgets to full applications
📚 Easy to master - Simple API, comprehensive documentation
🔧 Add to any project - No rewrites needed, progressive enhancement
🌟 Why Choose This?
Choose DOM Helpers Reactive if you want:
- Reactivity without framework overhead
- To add to existing projects easily
- No build step complexity
- Fine-grained control over updates
- To learn real JavaScript skills
- Direct DOM manipulation that's still reactive
- Small bundle size for performance
- Universal skills that transfer anywhere
💡 One-Line Summary
Make JavaScript objects reactive with zero setup - change data, UI updates automatically, using pure JavaScript and HTML.
🚀 Next Steps
Ready to start?
- Download the library - Grab the files you need
- Add script tags -
<script src="dh-reactive.min.js"></script> - Start coding - Create state, add effects, build things!
Join the community:
- Share your projects
- Ask questions
- Contribute improvements
- Help others learn
Welcome to reactive JavaScript made simple! 🎉
No frameworks. No build tools. No complexity. Just JavaScript with superpowers. ✨
DOM Helpers Reactive - Fine-grained reactivity for everyone