API Reference

Core Functions

These are the fundamental functions you'll use in every nuclo application.

update()

update(): void

Triggers a synchronous update of all reactive elements in the DOM. Call this function after mutating state to refresh the UI.

Usage

let count = 0;

button('Increment', on('click', () => {
  count++;
  update(); // Trigger UI update
}));

Notes

  • Updates are synchronous and happen immediately
  • Only reactive functions (zero-arg functions) are re-evaluated
  • Batch multiple state changes before calling update() for better performance
  • You can call update() as many times as needed, but batching is more efficient

render()

render(element: Element | Node, container: Element): void

Appends an element to a container in the DOM. This is typically used once at application startup.

Parameters

  • element: The element to render (created with tag builders like div())
  • container: The DOM element to append to (e.g., document.body)

Usage

const app = div(
  h1('My App'),
  p('Hello, world!')
);

render(app, document.body);

Notes

  • The element is appended to the container (not replaced)
  • You can call render() multiple times to mount different elements
  • Common pattern: render a single root element that contains your entire app

list()

list<T>(provider: () => T[], renderer: (item: T, index: number) => Node): DocumentFragment

Synchronizes an array to a list of DOM elements. Elements are reused when the same object reference appears in the array.

Parameters

  • provider: A function that returns the current array
  • renderer: A function that creates a DOM element for each item

Usage

let todos = [
  { id: 1, text: 'Learn nuclo', done: false },
  { id: 2, text: 'Build app', done: false }
];

list(() => todos, (todo, index) =>
  div(
    { className: () => todo.done ? 'done' : '' },
    span(() => `${index + 1}. ${todo.text}`),
    button('Toggle', on('click', () => {
      todo.done = !todo.done;
      update();
    }))
  )
);

How It Works

  • Tracks items by object identity (reference equality)
  • When you call update(), the list provider is called
  • nuclo compares the new array to the previous one
  • Elements are reused for items that are the same reference
  • New elements are created for new items
  • Elements are removed for items no longer in the array
  • Elements are reordered if items moved position

Important Notes

Object Identity Matters!

// ✓ Good: Mutate the object
todos[0].done = true;
update();

// ✗ Bad: Creates a new object, element will be recreated
todos[0] = { ...todos[0], done: true };
update();

// ✓ Good: Mutate the array
todos.push(newTodo);
todos.sort((a, b) => a.id - b.id);
update();

// ✓ Also good: Reassign with filtered array
todos = todos.filter(t => !t.done);
update();

Advanced Usage

// Nested lists
list(() => categories, category =>
  div(
    h3(category.name),
    list(() => category.items, item =>
      div(item.name)
    )
  )
);

// Filtered lists
list(() => todos.filter(t => !t.done), todo =>
  div(todo.text)
);

when()

when(condition: () => boolean, ...content: Renderable[]): ConditionalBuilder

Conditionally renders content based on a boolean condition. Supports chaining with .when() and .else().

Parameters

  • condition: A function that returns a boolean
  • content: Elements to render when the condition is true

Basic Usage

let isLoggedIn = false;

when(() => isLoggedIn,
  div('Welcome back!')
).else(
  div('Please log in')
);

Chaining Conditions

let role = 'user'; // 'admin' | 'user' | 'guest'

when(() => role === 'admin',
  div('Admin Panel')
).when(() => role === 'user',
  div('User Dashboard')
).else(
  div('Guest View')
);

Multiple Elements

when(() => showDetails,
  h2('Details'),
  p('Here are the details...'),
  button('Close', on('click', () => {
    showDetails = false;
    update();
  }))
);

How It Works

  • Evaluates conditions in order until one returns true
  • Renders the content for the first matching condition
  • If no conditions match and .else() is provided, renders else content
  • DOM elements are preserved if the active branch doesn't change
  • Elements are only created/destroyed when switching between branches

Performance Benefits

// Elements persist across updates if the same branch is active
let count = 0;

when(() => count > 0,
  div('Count is positive')  // This div stays alive while count > 0
).else(
  div('Count is zero or negative')
);

// Multiple updates with count > 0 won't recreate the div
count = 5; update();
count = 10; update();
count = 15; update();  // Same div element throughout

Nested Conditionals

when(() => user.isAuthenticated,
  when(() => user.hasPermission,
    div('Protected Content')
  ).else(
    div('Access Denied')
  )
).else(
  div('Please log in')
);

on()

on<K extends keyof HTMLElementEventMap>(
  event: K,
  handler: (event: HTMLElementEventMap[K]) => void,
  options?: AddEventListenerOptions
): Modifier

Attaches an event listener to an element. Returns a modifier that can be passed to tag builders.

Parameters

  • event: The event name (e.g., 'click', 'input', 'keydown')
  • handler: The event handler function
  • options: Optional event listener options (passive, capture, once)

Basic Usage

button('Click me',
  on('click', (e) => {
    console.log('Button clicked!', e);
  })
);

Multiple Events

input(
  on('input', (e) => {
    value = e.target.value;
    update();
  }),
  on('focus', () => {
    isFocused = true;
    update();
  }),
  on('blur', () => {
    isFocused = false;
    update();
  })
);

Event Options

// Passive event for better scroll performance
div(
  on('scroll', handleScroll, { passive: true })
);

// Capture phase
div(
  on('click', handleClick, { capture: true })
);

// One-time event
button('Click once',
  on('click', handleClick, { once: true })
);

Keyboard Events

input(
  on('keydown', (e) => {
    if (e.key === 'Enter') {
      handleSubmit();
    } else if (e.key === 'Escape') {
      handleCancel();
    }
  })
);

Form Events

form(
  on('submit', (e) => {
    e.preventDefault();
    handleFormSubmit();
  }),

  input({ type: 'text', name: 'username' },
    on('input', (e) => {
      username = e.target.value;
      update();
    })
  ),

  button({ type: 'submit' }, 'Submit')
);

Tag Builders

nuclo provides builder functions for all HTML and SVG elements. Each function creates and returns a DOM element.

Signature

tagName(...children: Renderable[]): HTMLElement
tagName(attributes: Attributes, ...children: Renderable[]): HTMLElement

Basic Usage

// Just children
div('Hello, world!');

// Attributes + children
div(
  { className: 'container', id: 'main' },
  'Hello, world!'
);

// Nested elements
div(
  h1('Title'),
  p('Paragraph'),
  ul(
    li('Item 1'),
    li('Item 2')
  )
);

HTML Tags

All standard HTML5 elements are available:

Document Structure

html, head, body, header, footer, main, section, article, aside, nav

Content Sectioning

h1, h2, h3, h4, h5, h6, div, span, p, blockquote, pre, code

Lists

ul, ol, li, dl, dt, dd

Forms

form, input, textarea, button, select, option, label, fieldset, legend

Tables

table, thead, tbody, tfoot, tr, th, td, caption, col, colgroup

Media

img, video, audio, source, track, canvas, svg

Interactive

a, button, details, summary, dialog

Text Formatting

strong, em, mark, small, del, ins, sub, sup, abbr, cite, q, kbd, samp, var

And 100+ more! See the full list in the source code.

SVG Tags

All SVG elements are available for creating scalable vector graphics:

svg, circle, ellipse, line, path, polygon, polyline, rect,
g, defs, use, symbol, marker, clipPath, mask, pattern,
linearGradient, radialGradient, stop, text, tspan, textPath

Usage

const icon = svg(
  { viewBox: '0 0 24 24', width: '24', height: '24' },
  circle({ cx: '12', cy: '12', r: '10', fill: 'blue' }),
  path({ d: 'M12 2 L12 22', stroke: 'white', 'stroke-width': '2' })
);

Attributes

Attributes are passed as an object. They can be static values or reactive functions.

Static Attributes

div({
  id: 'main',
  className: 'container',
  'data-test': 'value',
  'aria-label': 'Main content'
});

Reactive Attributes

div({
  className: () => isActive ? 'active' : 'inactive',
  'aria-pressed': () => isActive,
  disabled: () => !isValid,
  hidden: () => !isVisible
});

Style Attributes

// Object style
div({
  style: {
    color: 'red',
    fontSize: '16px',
    backgroundColor: () => isActive ? 'blue' : 'gray'
  }
});

// String style
div({
  style: 'color: red; font-size: 16px;'
});

// Reactive string style
div({
  style: () => `color: ${color}; font-size: ${size}px;`
});

Boolean Attributes

input({
  type: 'checkbox',
  checked: () => isChecked,
  disabled: () => isDisabled,
  required: true,
  readonly: false
});

Special Attributes

// className (maps to 'class')
div({ className: 'my-class' });

// htmlFor (maps to 'for')
label({ htmlFor: 'input-id' }, 'Label');

// Data attributes
div({ 'data-id': '123', 'data-type': 'user' });

Modifiers

Modifiers are special objects that can be passed to tag builders to perform additional operations on elements.

Event Modifiers

Created with the on() function:

button('Click',
  on('click', handleClick),
  on('mouseenter', handleHover)
);

Custom Modifiers

You can create custom modifiers for reusable element logic:

// Example: Focus modifier
function focus() {
  return {
    __modifier: true,
    apply(element) {
      requestAnimationFrame(() => element.focus());
    }
  };
}

// Usage
input(focus());

Type Definitions

All functions are fully typed with TypeScript. Add "types": ["nuclo/types"] to your tsconfig.json to get global type definitions.

For detailed type information, see the types directory in the repository.