Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

94 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

terminaltui

CI npm license node typescript tests website

Next.js for the terminal. Write a pages/ directory of TypeScript files. Get an interactive TUI with file-based routing, components, and themes. Distribute it with npx — or host it and let people ssh in.

🌐 terminaltui.dev  ·  📦 npm  ·  🚀 Try it: npx terminaltui try

terminaltui — 5-page tour rendered in a Synthwave-themed terminal: ANSI Shadow banner, navigable menu, components showcase, live theme switching, animated sparklines

Try it yourself: npx terminaltui try — this GIF predates the v2 renderer; the frames are provably identical, so it never needed re-recording.

Quick Start

npx terminaltui init my-site
cd my-site
npx terminaltui dev

Or try a built-in demo instantly — zero install, zero scaffold:

npx terminaltui demo restaurant

Requirements: Node.js >= 18 (relies on the built-in fetch). The package is ESM-only — load it with import, not require().


What is this?

terminaltui is a framework for building interactive terminal apps in TypeScript. You write pages, it handles routing, navigation, layout, state, and rendering. Users run your app with a single npx command, or you host it over SSH and they connect with ssh. No browser, no Electron, no React.

If you've used Next.js, you already know the shape: pages/about.ts becomes /about, pages/projects/[slug].ts is a dynamic route, api/stats.ts is a GET /api/stats endpoint.

How it compares:

terminaltui Ink Pastel Bubble Tea
Lang TypeScript TypeScript (React) TypeScript (Ink-based) Go
Shape Framework (pages, routing, layouts) Component library CLI command router TUI framework (Elm-style)
File-based routing for screens Yes No No (routes CLI subcommands) No
SSH hosting terminaltui serve (ssh2 peer dep) No No Via charmbracelet/wish
npx distribution First-class First-class First-class No (Go binary)
Components included 30+ Bring your own Inherits from Ink Via bubbles
AI codegen-native claude/SKILL.md ships in package No No No

What's new in 2.0

v2 gives terminaltui a production-grade engine: components now remember their state by where they live, not what they're labeled — so state survives navigation and two same-named accordions stop sharing a brain — and a new line-diffed renderer writes 67.7% fewer bytes to your terminal (269,355 → 87,027 across a 41-keypress scripted nav at 120x40), and exactly zero when nothing changed, which your users feel most over SSH. We didn't take the overhaul on faith: a dual-terminal oracle proved every final frame byte-identical to v1, and 3,323 tests across 52 suites — including a full demo sweep through a real PTY emulator — block every merge. Same pixels, a third of the bytes.

Migration note (breaking): component state (accordions, tabs, galleries, button loading) is now keyed by page + tree position instead of display label. Two same-labeled components no longer share state, and state persists across navigation — correct behavior, but observably different if your app relied on the old label sharing. Details in the CHANGELOG.


Project Structure

Each page is its own file. A top-level config.ts handles theme and settings.

my-site/
├── config.ts          # theme, banner, global settings
├── pages/
│   ├── home.ts        # landing page
│   ├── about.ts       # /about
│   ├── projects/
│   │   ├── index.ts   # /projects
│   │   └── [slug].ts  # /projects/:slug (dynamic route)
│   └── contact.ts     # /contact
├── api/
│   └── stats.ts       # GET /api/stats
└── components/        # reusable blocks

config.ts

import { defineConfig } from "terminaltui";

export default defineConfig({
  name: "My Site",
  theme: "cyberpunk",
  banner: { text: "MY SITE", font: "ANSI Shadow" },
});

pages/about.ts

import { card, timeline } from "terminaltui";

export const metadata = { label: "About", icon: "?" };

export default function About() {
  return [
    card({ title: "About Me", body: "Full-stack developer based in Portland." }),
    timeline([
      { date: "2024", title: "Started terminaltui" },
      { date: "2023", title: "Joined Acme Corp" },
    ]),
  ];
}

Features

30+ Components

Cards, tables, timelines, forms, progress bars, galleries, tabs, accordions, and more.

row([
  col([card({ title: "Revenue", body: "$1.2M" })], { span: 4 }),
  col([card({ title: "Users", body: "45,231" })], { span: 4 }),
  col([card({ title: "Uptime", body: "99.97%" })], { span: 4 }),
])

12-Column Grid System

Bootstrap-style responsive grid with automatic spatial navigation.

row([
  col([statsCard], { span: 3, xs: 12 }),
  col([chartCard], { span: 9, xs: 12 }),
], { gap: 1 })

Breakpoints: xs (<60 cols), sm (60-89), md (90-119), lg (>=120). Rows auto-wrap.

Spatial Navigation

Arrow keys move to the nearest item on screen -- like a TV remote. No configuration needed. Works automatically with all layouts.

Key Action
Up/Down or j/k Move to nearest item above/below
Left/Right or h/l Move to nearest item left/right
Enter Activate
Escape Go back
Tab Sequential fallback
1-9 Jump to page

10 Themes

theme switching

cyberpunk, dracula, nord, monokai, solarized, gruvbox, catppuccin, tokyoNight, rosePine, hacker. Plus custom themes.

export default defineConfig({ theme: "dracula" });

ASCII Art Engine

fonts and art

14 fonts, 15 scenes, 30+ icons, data visualization, image-to-ASCII conversion.

Forms & Inputs

TextInput, TextArea, Select, Checkbox, Toggle, RadioGroup, NumberInput, SearchInput, Button. Validation, submission, notifications.

File-Based API Routes

// api/stats.ts
export async function GET() {
  return { users: 45231, uptime: 99.97 };
}

File path maps to endpoint: api/stats.ts -> GET /api/stats. Framework starts a local server automatically.

Reactive State

const count = createState({ visitors: 0 });

dynamic(() => [text(`Visitors: ${count.get("visitors")}`)]);

createState, computed, dynamic, createPersistentState, fetcher, request, liveData.

Component state (accordions, tabs, galleries, button loading) is keyed by page + tree position — it survives navigation and refresh, stays isolated per page, and two same-labeled components never share state.

Rendering is line-diffed: only rows that changed are repainted, and a frame with no changes writes zero bytes.


Demos

No setup needed -- run any demo straight from npm:

npx terminaltui demo restaurant
npx terminaltui demo dashboard
npx terminaltui demo band
npx terminaltui demo coffee-shop
npx terminaltui demo conference
npx terminaltui demo developer-portfolio
npx terminaltui demo freelancer
npx terminaltui demo startup
npx terminaltui demo server-dashboard
npx terminaltui demo mac-monitor   # macOS only — live system stats
Demo Theme Highlights
Restaurant gruvbox Tabbed menu, reservation form, split layout
Dashboard hacker Live API data, persistent state, parameterized routes
Band rosePine Album cards, tour dates, mailing list
Coffee Shop catppuccin Tabbed menu, catering form
Conference nord Schedule tabs, speaker grid, sponsor tiers
Developer Portfolio cyberpunk Skill bars, sparklines, project grid
Freelancer custom Testimonial quotes, contact form
Startup tokyoNight Pricing tiers, feature accordion
Server Dashboard hacker System metrics, container table, log stream
Mac Monitor hacker Live macOS system stats (CPU/mem/GPU/disk/net/battery/processes), dynamic routes for per-process detail — darwin only

Restaurant

restaurant demo

Dashboard (live API data)

dashboard demo


CLI

terminaltui try                # run a 5-page guided tour — zero install, zero config
terminaltui init [tpl|name]    # scaffold a new project — arg is a template (minimal, portfolio, landing, restaurant, blog, creative) or your site name
terminaltui create             # interactive prompt builder — describe what you want, AI builds it
terminaltui convert            # drop terminaltui docs into your project for AI-assisted conversion
terminaltui validate           # check a file-based routing project for common issues
terminaltui dev [path]         # start development preview (auto-starts API server if routes defined)
terminaltui serve [path]       # host your TUI over SSH
terminaltui demo [name]        # run a built-in demo
terminaltui build              # bundle for npm publish (includes API routes)
terminaltui test               # run automated tests on the site in the current directory
terminaltui art                # manage art assets (list, preview, create, validate)
terminaltui help               # show help

SSH Hosting

Host any TUI app over SSH -- anyone can connect with ssh and see it rendered in their terminal, zero install required. Think ssh chat.shazow.net but for any terminaltui project.

v2 writes 67.7% fewer bytes per session, and idle frames cost zero bandwidth — over ssh, that's latency you can feel.

The serve command needs ssh2, an optional peer dependency — install it in your project first:

npm install ssh2
terminaltui serve --port 2222

If terminaltui is installed globally (or run via zero-install npx), it also picks up an ssh2 installed in the project directory you run serve from; alternatively, install it globally too with npm install -g ssh2.

Then from any machine:

ssh localhost -p 2222

Each connection gets an independent session. Arrow keys, forms, navigation -- everything works.

Flag Default Description
--port <N> 2222 SSH port
--host-key <path> .terminaltui/host_key Host key path (auto-generated)
--max-connections <N> 100 Max simultaneous connections

See It Live

npx omar-musayev

A real portfolio built with terminaltui.


Testing

terminaltui ships a headless emulator (terminaltui/emulator) — think Playwright, but the browser is a terminal. It spawns your app in a PTY, scripts keypresses, and lets you assert on the rendered grid:

import { TUIEmulator } from "terminaltui/emulator";

const emu = await TUIEmulator.launch({
  command: "npx terminaltui dev",
  cwd: "./my-site",
  cols: 120,
  rows: 40,
});

await emu.waitForBoot();
emu.assert.textVisible("MY SITE");

await emu.press("down");
await emu.press("enter");
await emu.waitForText("About Me");
emu.assert.currentPage("about");

// New in 2.0: assert your app's render cost in CI
emu.resetBytesReceived();
await emu.press("escape");
await emu.waitForIdle();
console.log(`repaint cost: ${emu.bytesReceived} bytes`);

await emu.close();

Beyond the basics: screen.text() / screen.cells() for raw grid access, screen.menu() / screen.cards() / screen.links() for structural queries, navigateTo() for menu-aware navigation, resize() for breakpoint tests, and a recorder for replayable scripts. It's the same emulator the framework's own test suite and the v2 render benchmark run through. See docs/testing.md.


Performance

Method: measured with the built-in emulator driving a 41-keypress scripted navigation at 120x40; medians of 3 runs. The metric is bytes written to the terminal — not a speed claim.

Scenario Change in bytes written (v1 → v2)
demos/startup −69.0%
demos/developer-portfolio −66.2%
Combined total 269,355 → 87,027 (−67.7%)
  • Frames with no changes write 0 bytes.
  • Equivalence, not vibes: a dual-VirtualTerminal oracle ran old and new renderers side by side and proved the final grids byte-identical.
  • Measure your own app the same way with TUIEmulator.bytesReceived (byte totals are comparable within the same PTY backend on the same machine).

For AI Agents

terminaltui ships with claude/SKILL.md -- a 2,500+ line API reference designed for AI code generation. The terminaltui create and terminaltui convert commands generate tailored prompts for Claude Code.


Documentation

Doc What's in it
docs/getting-started.md Install, scaffold, run your first project
docs/cli-reference.md Every CLI command with flags and examples
docs/components.md Component catalog with examples
docs/layouts.md Grid system, spatial navigation, layout patterns
docs/routing.md File-based routing, dynamic routes, middleware
docs/api-routes.md File-based HTTP API server (api/*.ts)
docs/state-data.md createState, computed, dynamic, fetcher, liveData
docs/themes.md The 10 built-in themes + how to write your own
docs/ascii-art.md Banners, scenes, icons, dataviz, image-to-ASCII
docs/serve.md SSH hosting (terminaltui serve)
docs/testing.md Headless TUI emulator
docs/create-command.md The interactive prompt builder
claude/SKILL.md Full API reference for AI code generation
CHANGELOG.md Version history
ARCHITECTURE.md Codebase structure and design decisions

Tech Stack

  • TypeScript -- strict mode, zero any in public API
  • 1 required dependency (esbuild) -- ssh2 is an optional peer dependency for serve
  • 3,323 tests across 52 suites, all blocking in CI -- the default subset (2,440 tests, 33 suites) runs in ~20s; the full sweep drives every demo through a real PTY emulator
  • Apple Terminal compatible -- auto-detects and uses 256-color fallback

Contributing

Issues and PRs welcome at github.com/OmarMusayev/terminaltui.

License

MIT

About

Framework for building interactive terminal apps — 30+ components, grid system, spatial navigation, themes, ASCII art, file-based routing, SSH hosting. Run via npx or host over SSH.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages