A dependency-free TypeScript terminal UI runtime. Screens are plain data; JSX is one way to write them.
npm install @textui/kitimport { Box, Text, render, useInput, useState } from '@textui/kit';
function Counter() {
const [count, setCount] = useState(0);
useInput((e) => {
if (e.name !== '+') return false;
setCount((c) => c + 1);
return true;
});
return (
<Box border="round" padding={1} direction="column">
<Text bold>Count: {count}</Text>
<Text dim>+ to increment, ctrl+c to quit</Text>
</Box>
);
}
const { waitUntilExit } = render(<Counter />);
await waitUntilExit();Status: pre-1.0. The surface is still moving.
Let's cut to the chase, shall we?
TextUI can go from a small interactive prompt to a full-screen terminal application.
→ Get started → Read the documentation
JSX compiles to data.
<Row gap={1}>
<Text>Hello</Text>
</Row>and:
{
component: 'Row',
gap: 1,
children: [
{ component: 'Text', children: ['Hello'] }
]
}describe the same screen.
A screen can be written in TypeScript, loaded from JSON, generated, edited or sent over a wire without the runtime changing.
Everything else follows from that: one reactive store addressed by paths, typed registries for components, commands, themes, shells and resources, and a renderer that diffs cells rather than redrawing frames.
One package is enough to have something on screen:
npm install @textui/kit
# pnpm add @textui/kit@textui/kit is the runtime, a terminal to put it on, and render.
Box, Text, the hooks and render all come from here. The example above needs nothing else.
render() mounts the application and returns a handle immediately:
const { app, waitUntilExit } = render(<App />);
await waitUntilExit();| On the handle | What it does |
|---|---|
app |
Commands, themes, focus, the store — everything hello world did not need |
waitUntilExit() |
Resolves when the application stops, however it stopped |
unmount() |
Stops the app, puts the terminal back, resolves waitUntilExit() |
rerender(node) |
Swaps the root |
For one frame and no terminal — a report, --help, a test — use renderOnce or renderToString instead.
Those return. render() runs.
For Panel, Table, Row, Column, charts, overlays, forms and the rest of the catalog, add @textui/widgets:
npm install @textui/widgets
# pnpm add @textui/widgetsComponents imported directly can be used directly:
import { Badge, Card } from '@textui/widgets';
function Status() {
return (
<Card title="Server">
<Badge label="Online" tone="success" />
</Card>
);
}Nothing to register.
When a screen names components in data, a string has to resolve to something. That is the case that needs the registry:
import { render } from '@textui/kit';
import { registerBuiltins } from '@textui/widgets';
render(<Dashboard />, {
onBoot: registerBuiltins,
});Forget it and nothing throws. The name resolves to nothing, and TextUI draws the name itself in red where the component should have been:
$ node dashboard.js
<Card>That red <Card> on screen is the error message: a missing registration, not a missing import.
For a project set up for you, and for components copied into your source rather than imported:
npx @textui/cli init
# pnpm dlx @textui/cli initAnd when something renders wrong:
npx @textui/cli doctordoctor tells you what this terminal can actually do: Unicode level, colour depth, keyboard protocol and the capabilities TextUI detected.
All six published packages use the same version and depend only on each other.
| Package | Version | What it is | Source |
|---|---|---|---|
@textui/kit |
One install: the runtime, a terminal and render. Start here. |
packages/facade |
|
@textui/core |
The runtime: store, registries, renderer, hooks and the four host primitives | packages/core |
|
@textui/widgets |
Component catalog: layout, display, controls, data, overlays and charts | packages/widgets |
|
@textui/terminal |
Terminal adapters, capability detection, ANSI writing and input decoding | packages/terminal |
|
@textui/testing |
Headless harness: semantic queries, input, resizing and time | packages/testing |
|
@textui/cli |
textui init / add / create / doctor, and primitives for your own CLI |
packages/cli |
Not published yet:
| Project | What it is |
|---|---|
@textui/documents |
Document buffers, resource viewers and content adapters |
@textui/textide |
An IDE that runs in a terminal, built on TextUI |
@textui/textide-git |
Git for TextIDE, as a loadable extension |
components/ |
The source-copy registry — components you own, not import |
playground/ |
The showcase, focused playgrounds and a filesystem explorer |
Nothing third-party is installed alongside it.
@textui/core has an empty dependencies field, and the packages above it depend only on each other — so what you audit is what you get, and the tree does not grow behind your back.
That also keeps the source close to running unbuilt. Node has erased types by default since 23.6, and most of this codebase is already erasable syntax.
The full argument, and what is still in the way, is in DEVELOPER.md.
Node >= 22, or Bun — the packages are plain ESM with no native code, and run on either.
TypeScript is how the examples are written rather than something TextUI needs. For TSX:
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "@textui/kit"
}
}See Getting started.
Published at softov.github.io/textui and readable in docs/ as plain Markdown.
Start here:
| Document | What it answers |
|---|---|
| Getting started | From nothing to a running application |
| The vocabulary | The words everything else assumes |
| Architecture | The model: store, graph, registries, surfaces, shells |
| Decisions and tradeoffs | What was chosen, and what it cost |
Then by subsystem:
| Section | What it covers |
|---|---|
| Store | Paths, scopes, computed, collections, providers, events |
| Components | The catalog, how to write one, and the templates |
| Themes | Tokens, glyphs, borders, capability downgrade, syntax |
| Platform | Commands, keybindings, focus, layers, screens, extension points |
| Terminal | Adapters, capabilities, managed and embedded sessions |
| Documents | Resource kinds, providers, viewers, editors, buffers |
| CLI | The developer CLI and the registry model |
| Testing | The harness, and what to assert |
Working on TextUI rather than with it — building, testing, the playgrounds, the docs site and how a release is cut — is in DEVELOPER.md.
MIT