Signals · JSX · SSR

Update the node, not the page.

Veloz is a reactive UI library where signals drive the exact DOM nodes that depend on them. No virtual DOM, no re-renders: components run once.

terminal
npm install @velozjs/dom
Quantity
2

Rendered DOM

  • <h4>Order summary0 updates
  • <p>2 items × $24.000 updates
  • <p>Subtotal $48.000 updates
  • <p>Discount —0 updates
  • <p>Shipping $9.000 updates
  • <strong>Total $57.000 updates

Component ran 1 time. Each row counts its own DOM updates.

Change the order. Only the rows whose text changes light up.

The model

Components run once

A component is a function that builds real DOM and returns it. What changes later are the functions you put in JSX: each one becomes a live region bound to the signals it reads.

  1. Read signals inside functions{count} or {() => a() + b()} update; values read in the component body don't.
  2. Props arrive as they areCallbacks and render props stay functions. Accept a value or a signal with access().
  3. Glitch-free by designMemos never go stale, effects run once per change, and everything is disposed with its owner.
Counter.tsx
import { render, createSignal, createMemo } from '@velozjs/dom';

function Counter() {
  console.log('runs once');
  const [count, setCount] = createSignal(0);
  const doubled = createMemo(() => count() * 2);

  return (
    <button onClick={() => setCount((c) => c + 1)}>
      {count} × 2 = {doubled}
    </button>
  );
}

render(() => <Counter />, document.body);
In the box

Everything a UI layer needs, nothing it doesn't

Fine-grained signals

Signals, memos and effects with batched, glitch-free updates.

SSR and hydration

renderToString on the server, hydrate on the client, same components.

Async data

createResource with Suspense, ErrorBoundary and data handed to hydration.

Scoped CSS-in-JS

css, styled and the css prop, CSP-friendly and collected during SSR.

Secure by default

Text is never HTML, URLs and attribute names are validated, innerHTML is sanitized.

Small and typed

ESM only, TypeScript-first, no runtime dependencies.

Async data

Loading states without the plumbing

Read a resource inside Suspense and the fallback shows until the first load finishes. Refetches keep the current content on screen; failures go to the nearest ErrorBoundary. On the server, renderToStringAsync waits for the data and hands it to hydrate().

tsx
const [user] = createResource(userId, (id) =>
  fetch(`/api/users/${id}`).then((r) => r.json())
);

<Suspense fallback={<Spinner />}>
  {() => <h2>{() => user()?.name}</h2>}
</Suspense>
Async data guide

Nothing loaded yet. The resource only fetches once its source is set.

Server rendering

This page is the demo

Every page here is rendered to HTML by @velozjs/ssr at build time and styled with salve-css. Only the interactive parts (the playground, the async demo, the theme switch and the menu) hydrate, so the rest ships without JavaScript.

Server rendering guide
  1. The server renders components to HTML with hydration keys and region markers.
  2. hydrate() reuses those elements, attaches events and effects, and removes the keys.
  3. From then on, signals update only the nodes that depend on them.
Packages

Take what you need

Your first component in three steps

Install the package, add the Vite plugin, write JSX.

Read the guide