Crust logoCrust

TUI

OpenTUI renderer lifecycle for Crust command actions.

@crustjs/tui is a small, renderer-agnostic adapter that gates terminal access, creates an OpenTUI renderer, and keeps a command action alive until the renderer is destroyed.

Bun only

@crustjs/tui requires Bun 1.4 or newer. Unlike the other Crust libraries, it does not support Node.js or Deno because OpenTUI uses a native Zig library over FFI.

Install

Any package manager can install the packages; the application still has to run on Bun. Install the adapter and OpenTUI core:

npm install @crustjs/tui @opentui/core

Then add one framework renderer. Core needs neither.

Solid
npm install @opentui/solid solid-js
React
npm install @opentui/react react

runTui(mount, config?)

function runTui(mount: TuiMount, config?: CliRendererConfig): Promise<void>;

runTui() requires both the configured (or process) stdin and stdout to be TTYs. It throws a NonInteractiveError before creating a renderer otherwise. Match this portable convention with error.name === "NonInteractiveError".

The renderer defaults to screenMode: "alternate-screen", consoleMode: "disabled", and exitOnCtrlC: true; explicit config values win. The promise resolves when the renderer is destroyed: call renderer.destroy() to quit (in components, via useRenderer() from @opentui/solid or @opentui/react). This also covers destruction while mount is still pending. A mounting error destroys the renderer before being rethrown.

With the default Ctrl+C handling, cancellation destroys the renderer and rejects with an error named "AbortError". Crust execute() maps that cancellation to exit code 130 without default error output. Set exitOnCtrlC: false when the application owns cancellation.

Solid

import { Crust } from "@crustjs/core";
import { runTui } from "@crustjs/tui";
import { render } from "@opentui/solid";

new Crust("app").action(() => runTui((r) => render(() => <App />, r)));

React

import { Crust } from "@crustjs/core";
import { runTui } from "@crustjs/tui";
import { createRoot } from "@opentui/react";

new Crust("app").action(() => runTui((r) => createRoot(r).render(<App />)));

Core

import { Crust } from "@crustjs/core";
import { runTui } from "@crustjs/tui";
import { TextRenderable } from "@opentui/core";

new Crust("app").action(() =>
  runTui((r) => {
    r.root.add(new TextRenderable(r, { content: "Hello" }));
  }),
);

Compiling

package.json
{
  "bin": { "app": "src/cli.tsx" },
  "crust": {
    "bunPlugins": ["@opentui/solid/bun-plugin"]
  }
}

The entry is the bin value. Solid needs OpenTUI's JSX transform at compile time, so list @opentui/solid/bun-plugin in crust.bunPlugins; the bunfig.toml preload covers bun run and build validation, not compilation. React and core apps compile with a plain crust build.

Cross-compiling needs every platform's native OpenTUI package installed: bun install --os "*" --cpu "*".

Compiled binaries do not load the bunfig.toml of the directory they start in, so the development preload cannot crash a compiled app (or crust itself) run from your project directory.

On this page