Rezi¶
Rezi is a code-first terminal UI framework for Node.js and Bun. Build interactive terminal applications with a declarative widget API, automatic focus management, and native-backed rendering through the Zireael C engine.
import { ui } from "@rezi-ui/core";
import { createNodeApp } from "@rezi-ui/node";
const app = createNodeApp<{ count: number }>({
initialState: { count: 0 },
});
app.view((state) =>
ui.page({
p: 1,
gap: 1,
header: ui.header({ title: "Counter", subtitle: "Beautiful defaults" }),
body: ui.panel("Count", [
ui.row({ gap: 1, items: "center" }, [
ui.text(String(state.count), { variant: "heading" }),
ui.spacer({ flex: 1 }),
ui.button({
id: "inc",
label: "+1",
intent: "primary",
onPress: () => app.update((s) => ({ count: s.count + 1 })),
}),
]),
]),
})
);
app.keys({ q: () => app.stop() });
await app.start();
Why Rezi?¶
- Declarative and Type-Safe
- Define your UI as a function of state. Full TypeScript support with strict typing for props, state, and events.
- Rich Widget Library
- 50+ built-in widgets covering forms, tables, trees, modals, code editors, command palettes, charts, and more.
- Native Performance
- Powered by the Zireael C engine with a binary protocol boundary. No virtual DOM diffing overhead.
- Focus Management
- Automatic keyboard navigation with focus zones, focus traps, and modal stacking.
- Mouse Support
- Click to focus and activate widgets, scroll wheel for lists and editors, drag to resize split panes, click backdrops to close modals. Detected automatically — no configuration needed.
- Theming
- Six built-in themes (dark, light, dimmed, high-contrast, nord, dracula) with semantic color tokens.
- Declarative Animation
- Built-in animation hooks (
useTransition,useSpring,useSequence,useStagger) plus container transition props onui.box(...). - Keybindings
- First-class support for modal keybindings with chord sequences (Vim-style
g g, Emacs-styleC-x C-s). - JSX Support
- Write widget trees using JSX syntax with
@rezi-ui/jsx— no React required. Getting started → - Performance
- See benchmarks → for methodology, limitations, and the latest committed results.
Architecture¶
flowchart TB
App["Application Code"] --> Core["@rezi-ui/core"]
JSX["@rezi-ui/jsx"] -.-> Core
Core --> Node["@rezi-ui/node"]
Node --> Native["@rezi-ui/native"]
Native --> Engine["Zireael C Engine"]
subgraph Runtime-Agnostic
Core
end
subgraph Node.js/Bun Runtime
Node
Native
end
| Layer | Purpose |
|---|---|
| @rezi-ui/core | Widgets, layout, themes, forms, keybindings. No Node.js APIs. |
| @rezi-ui/node | Node.js/Bun backend with worker and inline execution modes. |
| @rezi-ui/native | N-API addon (napi-rs) binding to the Zireael C rendering engine. |
| @rezi-ui/jsx | Optional JSX runtime for widget trees. |
Getting Started¶
-
Install
Install Rezi via npm and set up your first project.
-
Quickstart
Build a minimal Rezi application.
-
Widgets
Browse the complete widget catalog.
-
Styling
Themes, colors, and visual customization.
Core Concepts¶
State-Driven Rendering¶
Rezi applications are state-driven. You define a view function that returns a widget tree based on your application state:
type State = { items: string[]; selected: number };
app.view((state) =>
ui.column({ gap: 1 },
state.items.map((item, i) =>
ui.text(i === state.selected ? `> ${item}` : ` ${item}`, {
key: String(i),
})
)
)
);
State Updates¶
Update state with app.update(). Updates are batched and coalesced for efficiency:
Widget Composition¶
Widgets compose naturally. Container widgets like column, row, and box accept children:
ui.box({ title: "User Form", p: 1 }, [
ui.field({ label: "Name", required: true, children:
ui.input({ id: "name", value: state.name })
}),
ui.row({ gap: 2 }, [
ui.button({ id: "submit", label: "Submit", intent: "primary" }),
ui.button({ id: "cancel", label: "Cancel" }),
]),
])
Focus and Navigation¶
Interactive widgets (buttons, inputs) automatically participate in focus navigation. Use Tab/Shift+Tab to move between focusable elements, or click any focusable widget with the mouse:
ui.column({}, [
ui.button({ id: "first", label: "First" }), // Tab stop 1
ui.button({ id: "second", label: "Second" }), // Tab stop 2
ui.input({ id: "name", value: "" }), // Tab stop 3
])
Widget Categories¶
Primitives¶
Layout building blocks: text, box, row, column, spacer, divider
Form Inputs¶
Interactive controls: button, input, checkbox, radioGroup, select, field
Navigation¶
Routing and wayfinding: tabs, accordion, breadcrumb, link, pagination
Data Display¶
Data presentation: table, virtualList, tree, richText, badge, tag, status
Overlays¶
Modal UIs: modal, dropdown, layer, layers, toast, focusZone, focusTrap
Layout Components¶
Complex layouts: splitPane, panelGroup, resizablePanel
Advanced Widgets¶
Rich functionality: commandPalette, codeEditor, diffViewer, logsConsole, filePicker
Charts¶
Data visualization: gauge, progress, sparkline, barChart, miniChart
Graphics Components¶
Graphics rendering: canvas, image, lineChart, scatter, heatmap
Feedback¶
User feedback: spinner, skeleton, callout, errorDisplay, empty
Learn More¶
- Concepts - Understanding Rezi's architecture
- Ink to Ink-Compat Migration - Port existing Ink apps with minimal code churn
- Beautiful Defaults migration - Design system styling defaults and manual overrides
- Ink to Rezi Migration - Mental model mapping and migration recipes
- Lifecycle & Updates - State management patterns
- Routing - Page-level navigation and screen history
- Layout - Spacing, alignment, and constraints
- Input & Focus - Keyboard navigation and focus management
- Mouse Support - Click, scroll, and drag interactions
- Animation - Declarative motion hooks and box transitions
- Styling - Colors, themes, and visual customization
- Graphics - Capability-aware rendering and progressive enhancement
- Performance - Optimization techniques
- Debugging - Debug tools and frame inspection
- Architecture - Runtime stack and data flow
- API Reference - TypeDoc-generated API docs
- Developer Guide - Local setup and workflows