Skip to content

Pulse Framework

⚡ A DOM-first, fine-grained reactive framework for building UIs with JSX/TSX.

What is Pulse Framework?

Pulse Framework is a lightweight, reactive JavaScript framework that powers Odyssee Components. Unlike React or other Virtual DOM frameworks, Pulse takes a DOM-first approach where your JSX components return real DOM elements, not virtual representations.

"What You See Is What You Get"

When you write <MyComponent />, you get an actual HTMLElement or DocumentFragment immediately. No virtual DOM, no reconciliation, no re-renders.

Why Pulse?

  • 🎯 No Virtual DOM — Direct DOM manipulation with surgical precision
  • Fine-grained Reactivity — Only update what changed, never re-render entire components
  • 📦 Tiny Bundle — Minimal overhead, maximum performance (~5KB)
  • 🔧 Simple API — Signals, computed values, effects — that's it
  • 🎨 JSX/TSX First — Write declarative components with full TypeScript support
  • 🚀 Zero Magic — Predictable behavior, easy to debug
  • 🔥 No Hooks — No rules of hooks, no dependency arrays, no useState

Quick Example

tsx
import Pulse from 'pulse-framework';

const Counter: Pulse.Fn = () => {
  const count = Pulse.signal(0);
  const doubled = Pulse.computed(() => count() * 2);

  return (
    <div>
      <p>Count: {count}</p>
      <p>Doubled: {doubled}</p>
      <button onClick={() => count(count() + 1)}>Increment</button>
    </div>
  );
};

// Mount to DOM - it's a real HTMLElement!
document.body.appendChild(<Counter />);

That's it! No useState, no useEffect, no dependency arrays, no re-renders. The DOM updates automatically when signals change.

Core Concepts

Signals - Reactive State

Signals are reactive variables that automatically notify their dependents:

tsx
const name = Pulse.signal('Alice');

// Read with ()
console.log(name()); // "Alice"

// Write with ()
name('Bob');

// In JSX - DON'T call with ()
<p>Hello {name}</p>  // ✅ Correct - automatic binding
<p>Hello {name()}</p> // ❌ Wrong - breaks reactivity

Computed - Derived Values

Computed values automatically recalculate when dependencies change:

tsx
const firstName = Pulse.signal('John');
const lastName = Pulse.signal('Doe');

// Auto-updates when firstName or lastName changes
const fullName = Pulse.computed(() => `${firstName()} ${lastName()}`);

<p>{fullName}</p> // Always in sync

Effects - Side Effects

Effects run automatically when their dependencies change:

tsx
const count = Pulse.signal(0);

Pulse.effect(() => {
  console.log('Count changed:', count());
  document.title = `Count: ${count()}`;
});

count(5); // Effect runs automatically

How It Works

Pulse uses fine-grained reactivity instead of component re-renders:

  1. Signals track dependencies — Pulse knows exactly which DOM nodes depend on which signals
  2. Updates are surgical — Only the specific text nodes, attributes, or elements that need updating are changed
  3. No reconciliation — No virtual DOM diff, no key optimization needed (though we support keys)

Result: Blazing fast updates with minimal overhead and predictable behavior.

Comparison with React

FeaturePulseReact
Virtual DOM❌ No — Real DOM✅ Yes
Re-renders❌ Never✅ Component re-renders
Statesignal()useState()
Computedcomputed()useMemo()
Effectseffect()useEffect()
DependenciesAuto-trackedManual [deps]
JSX Syntax{signal} no (){state}
Component ReturnReal HTMLElementVirtual description
PerformanceSurgical updatesReconciliation diff

When to Use Pulse

Pulse is ideal for:

  • Building component libraries (like Odyssee Components)
  • High-performance UIs with frequent updates
  • Direct DOM manipulation requirements
  • Lightweight applications where bundle size matters
  • Developers who want simplicity without hooks complexity

Pulse may not be ideal for:

  • ❌ Server-side rendering (SSR) - currently limited support
  • ❌ React ecosystem integration (though you can wrap Pulse components)
  • ❌ Teams heavily invested in React patterns

Learn More

Explore the following guides to master Pulse Framework:

Getting Started

Examples

tsx
// Simple Counter
const Counter: Pulse.Fn = () => {
  const count = Pulse.signal(0);
  return (
    <button onClick={() => count(count() + 1)}>
      Count: {count}
    </button>
  );
};

// Form with Validation
const LoginForm: Pulse.Fn = () => {
  const email = Pulse.signal('');
  const password = Pulse.signal('');
  
  const isValid = Pulse.computed(() => 
    email().includes('@') && password().length >= 8
  );
  
  return (
    <form>
      <input type="email" value={email} 
        onInput={(e) => email((e.target as HTMLInputElement).value)} />
      <input type="password" value={password}
        onInput={(e) => password((e.target as HTMLInputElement).value)} />
      <button disabled={Pulse.computed(() => !isValid())}>
        Sign In
      </button>
    </form>
  );
};

// Dynamic List
const TodoList: Pulse.Fn = () => {
  const todos = Pulse.signal([
    { id: 1, text: 'Learn Pulse', done: false }
  ]);
  
  return (
    <ul>
      {Pulse.computed(() =>
        todos().map(todo => (
          <li key={todo.id}>{todo.text}</li>
        ))
      )}
    </ul>
  );
};

Philosophy

Pulse embraces simplicity and directness:

  • No abstraction layers — Work with real DOM elements
  • No magic — Behavior is predictable and debuggable
  • No complex rules — No rules of hooks, no stale closures
  • No performance cliffs — Fine-grained updates scale linearly

"What You See Is What You Get" — Your code is straightforward, your mental model is simple, and your application is fast.

Installation

Pulse Framework is included with Odyssee Components. If you're using Odyssee Components, you already have Pulse!

bash
npm install @odyssee/components

For standalone usage:

bash
npm install pulse-framework

TypeScript Configuration

Configure your tsconfig.json for JSX support:

json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "pulse-framework"
  }
}

Next Steps

Ready to dive deeper?

  1. Reactivity Guide — Master signals and computed values
  2. Components Guide — Build reusable components
  3. Browse Components — See Pulse in action with Odyssee Components

Happy Building! 🚀

Released under the MIT License.