CLI tool
RtlZeroMemory/Rezi avatar
RtlZeroMemory/Rezi

Rezi keeps TypeScript widget trees and pushes frames through a C terminal engine

Build TypeScript TUI backed by fast native rendering engine

677 stars16 forksTypeScriptApache-2.0

At a glance

What is it?
Rezi is a TypeScript framework for terminal applications on Node.js and Bun, where a widget tree is laid out in TypeScript, serialised into a binary drawlist, and diffed and written by Zireael, a C engine reached through an N-API binding. It is beta software with prebuilt native binaries, a versioned wire protocol and a stability tier per widget.
Who is it for?
Adopt Rezi if you are building a multi-panel terminal application on Node.js or Bun and want a structured app model, routed screens, focusable widgets and a testable rendering contract instead of a hand-rolled draw loop, and if you can accept beta semantics with per-widget stability tiers.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 128 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem Rezi targets is everything above a single line of output

The README states the target audience in one dense sentence: dashboards, control planes, internal tools, log viewers, agent consoles and developer workflows that need more than line-oriented output. The list of requirements that follow is the real specification: multi-panel layouts, routed screens, focusable controls, forms, tables, overlays, and predictable behaviour under keyboard and mouse input.

That last phrase is where the difference from a typical terminal library sits. A terminal UI is a program with state, a focus model and an event loop, and most of the pain in building one is not drawing boxes, it is deciding what happens when a key is pressed, which widget has focus, what a mouse click at those coordinates means, and how to test any of that without a terminal attached.

Rezi's answer is a structured app model rather than a rendering loop you assemble yourself, as the README puts it, plus a declarative widget API that does not require React. The repository topics list ink and react, which is a reasonable thing to see on a project that explicitly does not need them, since the comparison points are the tools most people reach for first.

The scope is a framework, not a library of widgets. Layout, focus and input handling, routing, widgets and testing tools are all part of it, and the framework presents itself as the thing that owns the terminal, not as a set of components you drop into a loop.

The drawlist boundary between TypeScript and C

The architecture has one interesting decision and it is worth describing precisely, because it explains both the performance story and the testing story.

Your application builds a widget tree in TypeScript. The core package lays it out and serialises the result into a compact binary drawlist. The Zireael engine, written in C, validates that drawlist, diffs it against the previous frame, and writes the minimal terminal update. In the other direction, the engine parses keyboard, mouse, paste and resize input into event batches that core routes through focus and keybindings.

So the boundary is a binary protocol, not a function call, and the protocol at that boundary is versioned and documented separately in docs/protocol/zrdl.md. The README says the API stays in TypeScript and the hot path stays in C, and that both sides are tested in isolation. That last point is the useful one: a fuzzed parser on either side of a native boundary is where a crash usually comes from, and the project treats it as a first-class test target.

The native side is reached through @rezi-ui/native, described as an N-API binding to the Zireael engine with prebuilt binaries. The README claims native framebuffer diffing and terminal output through a fuzz-tested C engine, and the repository carries a vendor directory, a native build script and a native vendor integrity check, which suggests the vendored source is verified rather than assumed.

Because the wire format is versioned and the drawlist is serialised rather than shared as memory, the architecture also has a natural test seam. A test can inspect frames without a terminal in sight, which is the subject of a later section.

Scaffolding an app and the widget API you write into

The quick start is a create command followed by a run command, with a Bun variant:

bash
npm create rezi my-app
cd my-app
npm run start
bash
bun create rezi my-app
cd my-app
bun run start

To add Rezi to a project that already exists, the README gives the two packages you need, the core widget API and the Node or Bun backend that provides terminal I/O, scheduling and native engine integration:

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

Three templates are available. The minimal one is a single-screen starter for focused utilities, the cli-tool one is a routed multi-screen starter for product-style terminal tools, and starship is a large command-console showcase with routing, charts, canvas, forms and overlays. The README tells you to start with minimal or cli-tool, and to use starship when you want the full surface area in one app, which is the same command with a template flag.

The widget API itself is plain function calls, not JSX, although an optional JSX runtime over the core API is published as @rezi-ui/jsx. A minimal application looks like this:

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

type State = { count: number };

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

The rest of the example registers a view function that receives the state and returns a page containing a header, a panel, a row with a text node, a spacer and a button whose onPress calls app.update. Keys are bound separately with app.keys, and the app starts with await app.run(). The pattern is a reducer plus a render function, with the framework owning the terminal and the event routing.

The first-party widget list covers forms, tables, trees, overlays, charts, file pickers and command palettes, which is a broad surface for a project still on a 0.1.0 beta line.

Inline screen mode, or the alternate buffer

Applications choose their presentation, and this is a decision the framework pushes onto you rather than a default. One option is full-screen on the alternate buffer, which is the familiar behaviour where the app takes over the whole terminal and restores your scrollback on exit. The other is inline screen mode, described as a bounded region on the primary screen with terminal scrollback preserved above it and the final frame left in scrollback on exit.

The README connects that second mode to a family of tools, the presentation style of agent CLIs, progress UIs and REPLs. This matters for anyone building a tool that coexists with a shell session, because the alternate buffer destroys the scrollback you were reading and inline mode does not. The mode is selected from the same widget API with a screen option, and the README shows the shape as a mode of inline with an inlineRows value, so the region is a fixed number of rows rather than the full window.

The costs of inline mode are the ordinary ones. The app is constrained to a bounded region, so a table with more rows than the region will scroll within it, and the interaction with the surrounding shell history is a rendering concern the framework can only partly own. The README points to a screen modes guide for the details.

The other side of the choice is that the same widget tree renders in either mode. The README lists full-screen and inline screen modes from the same widget API as one of its reasons for the project, and that is the point of a layout engine doing the work rather than a drawing helper you call with absolute coordinates.

Testability without a terminal, and the CI matrix behind it

The README treats testability as a design property rather than a feature. The core package includes a deterministic test renderer, and @rezi-ui/testkit adds assertions, frame snapshots and seeded fuzz helpers on top of node:test. Applications are testable without a terminal, which is the practical difference between a framework you can write unit tests for and one where every assertion needs a pseudo-terminal in the loop.

The project holds itself to a related bar. The README lists behavior-contract tests, golden-frame tests, fuzzed parsers on both sides of the native boundary, and a CI matrix covering Linux, macOS and Windows crossed with Node 18, 20 and 22 and Bun. The repository layout backs that up: a snapshots directory for golden frames, a benchmarks directory, a BENCHMARKS.md and a BENCHMARK_VALIDITY.md, a knip configuration for dead code detection, a dependency-cruiser configuration for dependency rules, and an API extractor script for the public surface.

That last item connects to the versioning story. The public API of @rezi-ui/core is recorded and diffed in CI, which is how a framework can promise that stable APIs do not change without a changelog entry. The package.json scripts show the same intent in the build: a codegen check runs before build, the create-rezi templates are checked and smoke-installed, core portability is checked, native vendor integrity is checked, and unicode sync is checked.

For a user, the practical benefit is that a rendering regression shows up as a diff in a snapshot file rather than as a visual surprise in a demo.

Beta semantics, stability tiers and where the sharp edges are

Rezi is in beta, and the README is specific about what that means rather than vague. The core API, covering the app model, layout, input routing, theming and the widget surface, is described as stable enough to build on, and the public API of @rezi-ui/core is recorded and diffed in CI. Breaking changes still happen before 1.0, but they are deliberate, batched and documented in the changelog with migration notes.

Individual widgets carry explicit stability tiers, documented in docs/widgets/stability.md, with the values stable, beta and experimental. The versioning section spells out the guarantees per tier: stable widgets and the core app, layout and routing APIs do not change documented behaviour without a changelog callout and a migration note, beta widgets are tested for their core invariants while contract details may still evolve, and experimental widgets may change at any time. Rezi follows semantic versioning applied through those tiers during the beta line.

The concrete limitation is the prebuilt binary coverage. The native package ships binaries for Linux x64 and arm64 with glibc, macOS x64 and arm64, and Windows x64 and arm64, so an npm install never compiles C. Alpine and musl is not yet supported, which rules out the smallest container images. Terminal capabilities, colour depth, mouse, bracketed paste and synchronized output, are probed at startup, and output adapts to what the terminal reports, so behaviour can differ between terminals in ways the application has to tolerate.

The last push to the repository was on 2026-06-17, and the recent tags are v0.1.0-beta.2 and v0.1.0-beta.1, both dated 2026-06-11, with v0.1.0-alpha.81 before that, so the release line is still moving under the beta version.

The alternative to all of this is writing the terminal handling yourself against raw escape sequences, or using an established library such as Ink, which is React-based and renders through its own reconciler. The difference in approach is that Ink gives you a React model, whereas Rezi gives you a native diffing engine and a binary protocol between the two halves, at the cost of being a much younger project.

Editorial conclusion

Adopt Rezi if you are building a multi-panel terminal application on Node.js or Bun and want a structured app model, routed screens, focusable widgets and a testable rendering contract instead of a hand-rolled draw loop, and if you can accept beta semantics with per-widget stability tiers. Do not adopt it for a line-oriented CLI that does not need focus handling or overlays, for Alpine or musl systems, which the README lists as not yet supported, or for a project that needs a guaranteed API today, since the framework is still on the 0.1.0 beta line where breaking changes are deliberate and batched. Verify first that your platform has a prebuilt binary, that the widgets you plan to use are marked stable rather than experimental in docs/widgets/stability.md, and that the version of @rezi-ui/core you pin matches the version of the other @rezi-ui packages, since all publishable packages share one version and release together.

Frequently asked questions

What is Rezi and how does it render terminal output?

Rezi is a TypeScript framework for terminal applications on Node.js and Bun. The core package lays out your widget tree and serialises it into a compact binary drawlist, and the Zireael engine, written in C, validates that drawlist, diffs it against the previous frame and writes the minimal terminal update.

How do I create a new Rezi app?

Run npm create rezi my-app, then cd my-app and npm run start, or the Bun equivalent with bun create rezi. To add Rezi to an existing project, install @rezi-ui/core and @rezi-ui/node. Three templates are available, minimal, cli-tool and starship.

Can I test a Rezi application without a terminal?

Yes. The core package includes a deterministic test renderer, and @rezi-ui/testkit adds assertions, frame snapshots and seeded fuzz helpers on top of node:test. The project also runs golden-frame tests, behavior-contract tests and fuzzed parsers on both sides of the native boundary.

Does Rezi work on Alpine Linux?

Not yet. The native package ships prebuilt binaries for Linux x64 and arm64 with glibc, macOS x64 and arm64, and Windows x64 and arm64, so an npm install never compiles C, but Alpine and musl is not yet supported.

How stable is the Rezi API before 1.0?

Rezi is in beta. Stable widgets and the core app, layout and routing APIs do not change documented behaviour without a changelog callout and a migration note, beta widgets may still evolve in contract details, and experimental widgets may change at any time, with the full tier assignment per widget in docs/widgets/stability.md.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. RtlZeroMemory/Rezi on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/rtlzeromemory-rezi.svg)](https://hysenlabs.com/projects/rtlzeromemory-rezi)

Was this useful?