Skip to content

Node/Bun backend

The Node/Bun backend owns:

  • native engine execution mode (auto | worker | inline)
  • frame scheduling and buffer pooling
  • transfer of drawlists to the engine and event batches back to core

Most apps should construct the app via:

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

const app = createNodeApp({
  initialState: { count: 0 },
  config: {
    executionMode: "auto",
    fpsCap: 60,
    maxEventBytes: 1 << 20,
  },
});

createNodeApp is the recommended path because it keeps core/backend config knobs aligned:

  • maxEventBytes is applied to both app parsing and backend transport buffers.
  • fpsCap is the single scheduling knob.
  • executionMode: "auto" resolves to inline when fpsCap <= 30, otherwise it prefers worker mode and falls back to inline for headless runs without a TTY or nativeShimModule.

Development hot reload:

  • createNodeApp({ hotReload: ... }) watches source files and hot-swaps either:
  • widget views via app.replaceView(...), or
  • route tables via app.replaceRoutes(...) for route-managed apps.
  • lifecycle is automatic: watcher starts/stops with app.start()/stop()/run().
  • app.hotReload exposes manual reloadNow() when needed.
  • low-level createHotStateReload(...) remains available for advanced manual wiring.
  • Raw draw mode remains excluded.

Execution mode details:

  • auto (default): select inline for low-fps workloads (fpsCap <= 30); otherwise prefer worker mode and fall back to inline when no TTY or nativeShimModule is available.
  • worker: force worker-thread engine execution. With the real native addon this requires an interactive TTY; test harnesses can provide nativeShimModule instead.
  • inline: run the engine inline on the main JS thread.

For headless snapshots and unit tests, prefer createTestRenderer() / @rezi-ui/testkit over the real native backend.

NO_COLOR support:

  • If NO_COLOR is present in the process environment, createNodeApp(...) forces a monochrome theme (no semantic color accents).
  • createNodeApp(...) returns an app object with app.isNoColor so app code can detect this mode.
import { createNodeApp } from "@rezi-ui/node";

const app = createNodeApp({ initialState: {} });
if (app.isNoColor) {
  // Optional app-level behavior for colorless terminals/CI.
}

Emoji width policy:

  • emojiWidthPolicy keeps core text measurement and native rendering aligned.
  • Allowed values:
  • "auto" (default): resolve from native overrides/env, optional probe, then fallback to "wide".
  • "wide": emoji clusters occupy at least 2 cells.
  • "narrow": emoji clusters occupy at least 1 cell.
  • Native overrides (nativeConfig.widthPolicy / width_policy) must match explicit policy values.
  • Optional terminal probe is disabled by default and only runs when ZRUI_EMOJI_WIDTH_PROBE=1 (opt-in to avoid startup input races).

Manual composition (advanced):

  • Prefer createNodeApp() for typical apps so related runtime knobs cannot drift.
  • If you compose manually with createNodeBackend() + createApp() (@rezi-ui/core), Rezi throws deterministic ZRUI_INVALID_PROPS errors when cursor/event/fps settings conflict.

Next: Worker model.