Skip to main content

Installation

npm install @rezi-ui/node @rezi-ui/core

Overview

@rezi-ui/node provides a production-ready backend for running Rezi applications on Node.js (18+) and Bun (1.3.0+). It includes:
  • Worker-thread based rendering engine
  • Terminal I/O management
  • Hot state reload for development
  • Image loading utilities
  • Stream adapters for useTail, useWebSocket, etc.

Quick Start

import { createNodeApp } from "@rezi-ui/node";
import { ui } from "@rezi-ui/core";

type State = { count: number };

const app = createNodeApp<State>({
  initialState: { count: 0 },
});

app.view((state) =>
  ui.page({ p: 1 }, [
    ui.text(`Count: ${state.count}`),
    ui.button({ id: "inc", label: "Increment" }),
  ])
);

app.on("press", (evt) => {
  if (evt.id === "inc") {
    app.update((s) => ({ count: s.count + 1 }));
  }
});

await app.run();

API Reference

createNodeApp

Create a fully configured Rezi app with Node.js backend.
options
CreateNodeAppOptions<S>
required
Configuration object for the app.
options.initialState
S
required
Initial application state.
options.routes
RouteDefinition<S>[]
Optional route definitions for page router.
options.initialRoute
string
Initial route path (default: first route).
options.theme
Theme | ThemeDefinition
Theme to use. Automatically converts to NO_COLOR when NO_COLOR env var is present.
options.config
NodeAppConfig
Optional configuration for app and backend behavior.
options.config.fpsCap
number
Frame rate cap (default: 60). Set to 30 for production apps to reduce CPU usage.
options.config.executionMode
'worker' | 'inline'
Execution mode (default: ‘worker’). Use ‘inline’ for debugging.
options.config.drawlistVersion
1 | 3 | 5
ZRDL protocol version (default: 5).
options.hotReload
NodeAppHotReloadOptions<S>
Development hot reload configuration.
Returns: NodeApp<S> - App instance with backend attached.
import { createNodeApp } from "@rezi-ui/node";
import { darkTheme } from "@rezi-ui/core";

const app = createNodeApp({
  initialState: { users: [] },
  theme: darkTheme,
  config: {
    fpsCap: 30, // Production setting
  },
});

NodeApp Type

The returned app instance extends the core App<S> with Node-specific properties:
backend
NodeBackend
The underlying Node.js backend instance.
isNoColor
boolean
True when NO_COLOR environment variable is present.
hotReload
HotStateReloadController | null
Hot reload controller when configured, null otherwise.

createNodeBackend

Low-level backend constructor. Prefer createNodeApp() for standard usage.
config
NodeBackendConfig
Backend configuration.
import { createNodeBackend } from "@rezi-ui/node";
import { createApp } from "@rezi-ui/core";

const backend = createNodeBackend({
  executionMode: "worker",
  fpsCap: 60,
});

const app = createApp({
  backend,
  initialState: {},
});

Hot State Reload

Development feature for hot-reloading view and route modules without losing state.

Configuration

import { createNodeApp } from "@rezi-ui/node";
import { watch } from "node:fs";

const app = createNodeApp({
  initialState: { count: 0 },
  hotReload: {
    // Watch view file
    view: {
      path: "./src/view.ts",
      exportName: "view", // Named export to reload
    },
    // Optional: Watch routes
    routes: {
      path: "./src/routes.ts",
      exportName: "routes",
    },
    // Optional: Custom file watcher
    watcher: (paths, onChange) => {
      // Custom watch implementation
      return () => {}; // Cleanup
    },
  },
});

await app.run();
// HSR automatically starts/stops with app lifecycle

Manual Control

import { createHotStateReload } from "@rezi-ui/node";

const hsr = createHotStateReload(app, {
  view: { path: "./src/view.ts", exportName: "view" },
});

await hsr.start();
// ... app runs with HSR active
await hsr.stop();
hotReload.start
() => Promise<void>
Start watching for file changes.
hotReload.stop
() => Promise<void>
Stop watching and cleanup.
hotReload.reload
() => Promise<void>
Manually trigger a reload.

Image Loading

loadImage
function
Load image from file system or URL for use in ui.image().
import { createNodeApp, loadImage } from "@rezi-ui/node";
import { ui } from "@rezi-ui/core";

const app = createNodeApp({ initialState: {} });

app.view(async () => {
  const logo = await loadImage("./logo.png");

  return ui.page({ p: 1 }, [
    ui.image({
      data: logo.data,
      width: logo.width,
      height: logo.height,
      fit: "contain",
    }),
  ]);
});
Supported formats: PNG, JPEG, WebP, GIF (via image decoding)

Stream Utilities

createNodeTailSource

Create a tail source for useTail hook to follow file updates.
import { createNodeApp, createNodeTailSource } from "@rezi-ui/node";
import { defineWidget, useTail, ui } from "@rezi-ui/core";

const LogViewer = defineWidget(() => {
  const logs = useTail(ctx, {
    source: createNodeTailSource("/var/log/app.log"),
    maxLines: 100,
  });

  return ui.column(logs.lines.map((line) => ui.text(line)));
});

Configuration Options

Execution Modes

executionMode
'worker' | 'inline'
  • worker (default): Engine runs in worker thread for best performance
  • inline: Engine runs in main thread for easier debugging

Frame Transport

frameTransport
'copy' | 'sab'
  • copy (default): Frame data copied to worker
  • sab: Use SharedArrayBuffer for zero-copy frame submission (experimental)

Performance Tuning

import { createNodeApp } from "@rezi-ui/node";

const app = createNodeApp({
  initialState: {},
  config: {
    // Frame rate
    fpsCap: 30, // Lower for production apps

    // Buffer sizes
    maxEventBytes: 8192, // Event batch buffer size
    maxDrawlistBytes: 1024 * 1024, // Max drawlist size (1MB)

    // Worker thread settings (when executionMode: 'worker')
    frameSabSlotCount: 2, // SAB ring buffer slots
    frameSabSlotBytes: 512 * 1024, // Bytes per slot
  },
});

NO_COLOR Support

createNodeApp automatically detects the NO_COLOR environment variable and converts any provided theme to monochrome:
# Force monochrome output
NO_COLOR=1 node dist/main.js
import { createNodeApp } from "@rezi-ui/node";
import { draculaTheme } from "@rezi-ui/core";

const app = createNodeApp({
  initialState: {},
  theme: draculaTheme, // Automatically converted to mono when NO_COLOR=1
});

console.log(app.isNoColor); // true when NO_COLOR is set

TypeScript Types

import type {
  NodeApp,
  NodeAppConfig,
  NodeBackend,
  NodeBackendConfig,
  CreateNodeAppOptions,
  HotStateReloadController,
  HotStateReloadOptions,
} from "@rezi-ui/node";

Runtime Requirements

  • Node.js: 18.0.0 or higher
  • Bun: 1.3.0 or higher
  • Platform: Linux, macOS, Windows (with limitations)

@rezi-ui/core

Core framework APIs

Getting Started

Installation guide

App Lifecycle

Understanding app lifecycle

Worker Model

Backend architecture details

Build docs developers (and LLMs) love