Frequently Asked Questions¶
General¶
What is Rezi?¶
Rezi is a code-first terminal UI framework for Node.js. It provides a declarative widget API for building rich terminal applications with features like automatic focus management, theming, and keyboard navigation.
Is Rezi like React for the terminal?¶
No. While Rezi uses a declarative widget tree similar to React's component model, it has a fundamentally different architecture:
- No virtual DOM diffing
- No hooks or component lifecycle
- Binary protocol boundary with a native C engine
- Deterministic rendering with no side effects in the view function
Rezi is designed specifically for terminal UIs, not as a React port.
What platforms does Rezi support?¶
Rezi supports:
- Linux: x64, arm64 (glibc)
- macOS: x64 (Intel), arm64 (Apple Silicon)
- Windows: x64
Prebuilt native binaries are included for all supported platforms.
What Node.js versions are supported?¶
Rezi requires Node.js 18 or later (18.18+ recommended).
Architecture¶
Why does Rezi use a native C engine?¶
The Zireael C engine provides:
- Fast framebuffer diffing and terminal output
- Terminal capability detection
- Platform-specific optimizations
- A strict binary ABI boundary (drawlist in, events out)
This architecture enables high performance while keeping the TypeScript code portable.
Does @rezi-ui/core work outside Node.js?¶
@rezi-ui/core is runtime-agnostic by design. It contains no Node.js-specific APIs (no Buffer, worker_threads, fs, etc.).
The Node.js integration is provided by @rezi-ui/node. Additional backends for other runtimes (Deno, Bun, etc.) could be implemented using the same core package.
What is the binary protocol?¶
Rezi uses two binary formats for communication with the native engine:
- ZRDL (Drawlist): Rendering commands sent from TypeScript to the engine
- ZREV (Event Batch): Input events sent from the engine to TypeScript
Both formats are versioned, little-endian, and 4-byte aligned. See the Protocol documentation for details.
Usage¶
How do I update application state?¶
Use app.update() with either a new state object or an updater function:
// Direct state
app.update({ count: 5 });
// Updater function (recommended for derived state)
app.update((prev) => ({ count: prev.count + 1 }));
Updates are batched and coalesced for efficiency.
How do I handle keyboard input?¶
Use app.keys() to register keybindings:
app.keys({
"ctrl+s": () => save(),
"ctrl+q": () => app.stop(),
"j": () => moveDown(),
"k": () => moveUp(),
});
For modal keybindings (like Vim modes), use app.modes():
app.modes({
normal: {
"i": () => app.setMode("insert"),
"j": () => moveCursorDown(),
},
insert: {
"escape": () => app.setMode("normal"),
},
});
app.setMode("normal");
How do I style widgets?¶
Use the style prop with RGB colors and text attributes:
import { rgb } from "@rezi-ui/core";
ui.text("Error", {
style: {
fg: rgb(255, 100, 100),
bold: true,
},
});
Or use built-in themes:
How do I show a modal dialog?¶
Use the ui.layers() and ui.modal() widgets:
ui.layers([
// Main content
MainScreen(state),
// Modal (conditionally rendered)
state.showModal && ui.modal({
id: "confirm",
title: "Confirm Action",
content: ui.text("Are you sure?"),
actions: [
ui.button({ id: "yes", label: "Yes", onPress: handleConfirm }),
ui.button({ id: "no", label: "No", onPress: closeModal }),
],
onClose: closeModal,
}),
])
How do I render a list efficiently?¶
For small lists, use ui.column() with mapped items:
For large lists (hundreds or thousands of items), use ui.virtualList():
ui.virtualList({
id: "items",
items: largeList,
itemHeight: 1,
renderItem: (item, index, focused) =>
ui.text(focused ? `> ${item.name}` : ` ${item.name}`),
})
Debugging¶
How do I debug rendering issues?¶
Enable the debug controller:
import { createDebugController, categoriesToMask } from "@rezi-ui/core";
const debug = createDebugController();
await debug.enable({
minSeverity: "info",
categoryMask: categoriesToMask(["frame", "error"]),
});
// Listen for errors
debug.on("error", (err) => console.error(err));
Use the debug panel widget:
import { debugPanel, fpsCounter, errorBadge } from "@rezi-ui/core";
ui.layers([
MainContent(),
debugPanel({ position: "bottom-right" }),
])
My app is slow. How do I optimize it?¶
- Use
virtualListfor long lists - Provide
keyprops for dynamic lists to enable efficient reconciliation - Avoid creating new objects/functions in the view function
- Check the debug controller for frame timing information
See the Performance guide for detailed optimization strategies.
Troubleshooting¶
"Error: Cannot find module '@rezi-ui/native'"¶
This usually means the native addon failed to load. Check:
- You have a supported platform (Linux/macOS/Windows x64 or arm64)
- Node.js version is 18 or later (18.18+ recommended)
- Try reinstalling:
npm install @rezi-ui/node
Colors don't display correctly¶
Your terminal may not support true color (24-bit RGB). Rezi requires a terminal with true color support.
Test your terminal:
If this shows orange text, your terminal supports true color.
Keybindings don't work¶
- Ensure your keybindings are registered with
app.keys()orapp.modes() - Check if focus is on an input widget (inputs capture key events)
- Verify the key syntax (e.g.,
"ctrl+s", not"Ctrl+S")
Can I use React/JSX with Rezi?¶
Yes, there are two options:
@rezi-ui/jsx— Native JSX runtime that maps JSX elements directly to Rezi VNodes. No React required. Best for new projects that prefer JSX syntax. See the JSX guide.@rezi-ui/ink-compat— Drop-in Ink compatibility layer that uses React andreact-reconcilerto bridge Ink components to Rezi. Best for migrating existing Ink apps. See the migration guide.
Can I migrate my Ink app to Rezi?¶
Yes. Install @rezi-ui/ink-compat and change your imports from "ink" to "@rezi-ui/ink-compat". All Ink components and hooks are supported. See the Ink migration guide.
How fast is Rezi compared to Ink?¶
Rezi native is 1,800x–40,000x faster than Ink depending on the scenario. Even using the Ink compatibility layer (@rezi-ui/ink-compat), you get 7–200x speedup with zero code changes. See the benchmark results.
What is Zireael?¶
Zireael is the C terminal rendering engine that powers Rezi. It handles all terminal I/O, framebuffer diffing, and rendering. Rezi communicates with Zireael via binary protocols (ZRDL drawlists and ZREV event batches).
More Questions?¶
- Check the documentation
- Open an issue on GitHub