@rezi-ui/ink-compat
v0.1.0-alpha.63
Published
Drop-in Ink compatibility layer powered by Rezi's rendering engine.
Readme
@rezi-ui/ink-compat
@rezi-ui/ink-compat is an Ink API compatibility layer powered by Rezi.
It keeps the Ink component/hook model, but replaces Ink's renderer backend with Rezi's deterministic layout + draw pipeline.
Why use it
- Keep existing Ink app code and mental model.
- Migrate incrementally (explicit import swap or package aliasing).
- Get deterministic, env-gated diagnostics for parity and performance triage.
Install
npm install @rezi-ui/ink-compatIf your app uses ink-gradient or ink-spinner, install matching shims:
npm install ink-gradient-shim ink-spinner-shimMigration options
Option A: explicit import swap
// Before
import { render, Box, Text } from "ink";
// After
import { render, Box, Text } from "@rezi-ui/ink-compat";Option B: no-source-change package aliasing
Keep import "ink" in app code and alias dependencies:
npm install \
ink@npm:@rezi-ui/ink-compat@latest \
ink-gradient@npm:ink-gradient-shim@latest \
ink-spinner@npm:ink-spinner-shim@latestEquivalent with pnpm:
pnpm add \
ink@npm:@rezi-ui/ink-compat@latest \
ink-gradient@npm:ink-gradient-shim@latest \
ink-spinner@npm:ink-spinner-shim@latestEquivalent with yarn:
yarn add \
ink@npm:@rezi-ui/ink-compat@latest \
ink-gradient@npm:ink-gradient-shim@latest \
ink-spinner@npm:ink-spinner-shim@latestVerify wiring (avoid silent fallback to real Ink)
Run this in the app root:
node -e "const p=require('ink/package.json'); if(p.name!=='@rezi-ui/ink-compat') throw new Error('ink resolves to '+p.name); console.log('ink-compat active:', p.version);"And confirm resolved path:
node -e "const fs=require('node:fs'); const path=require('node:path'); const pkg=require.resolve('ink/package.json'); console.log(fs.realpathSync(path.dirname(pkg)));"How it works
At runtime, ink-compat runs this pipeline:
- React reconciles to an
InkHostNodetree (compat host config). - Translation maps Ink props/components to Rezi VNodes.
- Rezi layout + render generate draw ops, then ANSI output is serialized to terminal streams.
Key behavior:
<Static>is handled as a dedicated scrollback-oriented channel.- Input/focus/cursor are bridged through compat context/hooks.
- Diagnostics and heavy instrumentation are env-gated.
For full architecture details, see https://rezitui.dev/docs/architecture/ink-compat/.
For a practical migration workflow, see https://rezitui.dev/docs/migration/ink-to-ink-compat/.
Supported API surface
Components
BoxTextNewlineSpacerStaticTransform
Hooks
useAppuseInputuseFocususeFocusManageruseStdinuseStdoutuseStderruseIsScreenReaderEnableduseCursor
Runtime APIs
renderrenderToStringmeasureElementResizeObservergetBoundingBoxgetInnerHeightgetScrollHeight
Keyboard helpers
kittyFlagskittyModifiers
Testing entrypoint
@rezi-ui/ink-compat/testing
render(element, options) options
stdout,stdin,stderrexitOnCtrlCpatchConsoledebugmaxFpsconcurrent(compatibility flag; not an upstream-concurrency semantic toggle)kittyKeyboardisScreenReaderEnabledonRenderalternateBufferincrementalRendering
Diagnostics
Trace output is env-gated:
INK_COMPAT_TRACE=1INK_COMPAT_TRACE_FILE=/path/logINK_COMPAT_TRACE_STDERR=1INK_COMPAT_TRACE_DETAIL=1INK_COMPAT_TRACE_DETAIL_FULL=1INK_COMPAT_TRACE_ALL_FRAMES=1INK_COMPAT_TRACE_IO=1INK_COMPAT_TRACE_RESIZE_VERBOSE=1INK_GRADIENT_TRACE=1
Debugging runbook:
https://rezitui.dev/docs/dev/ink-compat-debugging/
Known boundaries
- Minor visual differences can occur across terminal emulators / OS TTY behavior.
- App/version-specific messaging differences are expected and are not renderer bugs.
- Gradient interpolation can differ slightly while preserving overall behavior.
Documentation
- Porting guide:
https://rezitui.dev/docs/migration/ink-to-ink-compat/ - Architecture and internals:
https://rezitui.dev/docs/architecture/ink-compat/ - Debugging and parity runbook:
https://rezitui.dev/docs/dev/ink-compat-debugging/
