nuclo

A simple, explicit DOM library for building reactive user interfaces.

Build reactive UIs without the magic. Just functions, plain JavaScript objects, and explicit update() calls. No virtual DOM, no complex state management required.

import 'nuclo';

let count = 0;

const counter = div(
  h1(() => `Count: ${count}`),
  button('Increment', on('click', () => {
    count++;
    update();
  }))
);

render(counter, document.body);

Why nuclo?

🎯 Explicit & Predictable

You control when updates happen with a simple update() call. No surprises, no magic.

🚀 Direct DOM Manipulation

Work directly with the DOM. No virtual layer, maximum efficiency.

📦 Tiny Footprint

Minimal bundle size means faster load times and better performance.

🌍 Global Tag Builders

Natural API with global functions for all HTML and SVG elements.

📘 TypeScript-First

Full type definitions for all 140+ HTML and SVG tags out of the box.

⚡ Fine-Grained Reactivity

Only updates what changed, nothing more. Maximum efficiency.

Quick Start

Installation

npm install nuclo

Usage

Simply import once to register all global functions:

import 'nuclo';

// Now use div(), update(), on(), list(), when(), render(), etc. globally
let count = 0;
const app = div(
  h1(() => `Count: ${count}`),
  button('Click', on('click', () => { count++; update(); }))
);
render(app, document.body);

TypeScript Setup

Add to your tsconfig.json:

{
  "compilerOptions": {
    "types": ["nuclo/types"]
  }
}

Or in your vite-env.d.ts:

/// <reference types="nuclo/types" />

Core Concepts

1. Explicit Updates

nuclo doesn't auto-detect changes. You call update() when ready:

let name = 'World';

// Mutate freely
name = 'Alice';
name = name.toUpperCase();

// Update once when ready
update();

Advantages:

  • Performance: Batch multiple mutations into a single update cycle
  • Control: You decide exactly when the UI should refresh
  • Predictability: Zero surprise re-renders, explicit update flow
  • Simplicity: No proxies, no dependency graphs, just objects and functions
  • Debugging: Set a breakpoint at update() to trace all state changes

2. Reactive Functions

Zero-arg functions become reactive:

let count = 0;

div(
  () => `Count: ${count}`,  // Updates when update() is called
  { title: () => `Current: ${count}` }  // Attributes too
)

3. Conditional Rendering

First matching condition wins:

when(() => user.isAdmin,
  div('Admin Panel')
).when(() => user.isLoggedIn,
  div('User Dashboard')
).else(
  div('Please log in')
)

4. List Synchronization

Lists use object identity to track items:

list(() => items, (item, index) =>
  div(() => `${index}: ${item.name}`)
)

Examples

Counter

The classic example showing state management and event handling

View Example

Todo List

Dynamic lists with add, toggle, and delete operations

View Example

Search Filter

Real-time filtering with computed values

View Example

Async Loading

Handling async operations and loading states

View Example