Skip to content

Sponsor

Sponsor

DOM Helpers Reactive Library

Fine-grained reactivity for vanilla JavaScript - No framework, no build tools, just JavaScript

Quick Start (30 seconds)

html
<!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.

javascript
// 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 efficient

Visual 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 work

Direct DOM Approach:

Your Code

Dependency Tracking (Automatic)

Real DOM (Directly!)

⚡ Immediate, efficient

Who 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() and filter()

✅ Perfect for React Developers

Coming from React? You'll feel right at home:

javascript
// React hooks-like pattern
const count = ref(0);

effect(() => {
  console.log(count.value); // Like useEffect
});

// Computed values
const doubled = computed(() => count.value * 2); // Like useMemo

But simpler - no rules of hooks, no re-rendering, no reconciliation.

✅ Perfect for Vue Developers

Coming from Vue? This will feel familiar:

javascript
// 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:

javascript
// 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:

bash
npm create my-app
cd my-app
npm install
npm run dev
# Wait for build...
# Finally code after 5 minutes

DOM Helpers Reactive:

html
<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:

javascript
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:

html
<!-- 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:

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:

javascript
// 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:

html
<!-- 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:

html
<!-- 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:

html
<!-- 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:

html
<!-- 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 object
  • effect() - 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.

javascript
// ✅ 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:

javascript
// 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

javascript
const state = ReactiveUtils.state({ count: 0 });
// Proxy wraps the object

2️⃣ You read a property in an effect

javascript
effect(() => {
  console.log(state.count); // 👀 Proxy sees this!
});
// Proxy records: "This effect depends on 'count'"

3️⃣ You change the property

javascript
state.count = 10; // ✍️ Proxy sees this too!

4️⃣ Proxy automatically re-runs effects

javascript
// Proxy: "count changed, these effects depend on it"
// → Re-runs effect
// → Console logs: 10

The 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:

javascript
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 updates

This is fine-grained reactivity - surgical precision, no collateral updates.

Complete Feature Set

🎯 Core Reactivity

Reactive State

javascript
const user = state({ name: 'Alice', age: 25 });
user.name = 'Bob'; // ✨ All dependent effects run automatically

Effects - Auto-run code when dependencies change

javascript
effect(() => {
  document.getElementById('name').textContent = user.name;
}); // Runs now and whenever user.name changes

Computed Properties - Derived values that update automatically

javascript
user.computed('greeting', function() {
  return `Hello, ${this.name}!`;
});
console.log(user.greeting); // "Hello, Bob!" (auto-updates)

Watchers - React to specific property changes

javascript
watch(user, 'age', (newAge, oldAge) => {
  console.log(`Age changed from ${oldAge} to ${newAge}`);
});

Batching - Group multiple updates for efficiency

javascript
batch(() => {
  user.name = 'Charlie';
  user.age = 30;
  user.email = 'charlie@example.com';
}); // All effects run once, not three times

Deep Reactivity - Nested objects are automatically reactive

javascript
const app = state({
  user: { profile: { name: 'Alice' } }
});

app.user.profile.name = 'Bob'; // ✨ Works! Deep reactivity

🎨 DOM Integration

Declarative Bindings

javascript
// 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

javascript
// 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

javascript
updateAll(state, {
  count: 10,                    // State update
  '#display': { textContent: '10' }  // DOM update
});

📦 Data Structures

Refs - Simple reactive values

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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 KB

Cross-Tab Synchronization

javascript
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

javascript
// 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 keys

Watch Storage Keys

javascript
// 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

javascript
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

javascript
const collector = collector();

collector.add(effect(() => { /* ... */ }));
collector.add(watch(state, 'count', () => { /* ... */ }));
collector.add(() => console.log('Custom cleanup'));

// Clean up everything
collector.cleanup();

Cleanup Scopes

javascript
const cleanup = scope((collect) => {
  // All effects are automatically collected
  collect(effect(() => { /* ... */ }));
  collect(watch(state, 'key', () => { /* ... */ }));
});

// One call cleans up everything
cleanup();

Error Boundaries

javascript
// 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

javascript
// 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

javascript
// 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

javascript
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

javascript
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

javascript
effect(() => {
  console.log(state.count); // Tracked
  
  untrack(() => {
    console.log(state.other); // NOT tracked
  });
});

Manual Notify

javascript
state.count = 5;
notify(state, 'count'); // Manually trigger effects

Architecture 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 styles

Total Size: ~33KB for everything (10KB gzipped)
Core Only: 8KB (3KB gzipped)

Loading Strategies

Loading Complete Library

html
<script src="dh-reactive.min.js"></script>

Minimal (Just reactivity - 8KB):

html
<script src="01_dh-reactive.min.js"></script>

Recommended (Core + shortcuts + cleanup - 13KB):

html
<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):

html
<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:

html
<!-- 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:

html
<!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:

html
<!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:

  1. Created reactive state with state() and collection()
  2. Added computed properties that auto-update
  3. Set up effects that auto-update the DOM
  4. Wrote regular JavaScript functions
  5. Used regular event listeners

No build tools. No compilation. No special syntax. Just JavaScript!

Adding to Existing Projects

Plain JavaScript Project

html
<!-- 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

html
<!-- 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

html
<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)

html
<!-- 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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
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

javascript
// 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

javascript
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

javascript
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:

javascript
// 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:

javascript
// 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:

javascript
// 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:

javascript
// 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?

  1. Download the library - Grab the files you need
  2. Add script tags - <script src="dh-reactive.min.js"></script>
  3. 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