Running in a Terminal

Run the same dashboard full-screen in a terminal with text run.

This chapter's app is running in your console. Open it with ⌘⌥JCtrl+Shift+J⌘⌥KCtrl+Shift+K⌘⌥C (turn on developer features first), then try it.
Watch the chapter3:31

In this chapter

  • `text run`
  • Quit with `q`
  • Your own CLI
  • What's different

text run runs the same app in a terminal. Layout, components, keymaps and route handlers are shared, and the output is truecolor ANSI instead of %c CSS spans.

text run

Add a script and run it:

package.json+2-1json
  "scripts": {    "dev": "text dev",    "build": "text build",Removed:     "start": "text start"Added:     "start": "text start",Added:     "tui": "text run"  },
bun run tui                        # render ./app at /bun run tui -- --path /deployments # start on another route

text run imports every file in app/, mounts the App Router with an in-memory history and draws it full-screen. <Link>, useRouter(), notFound() and redirect() work unchanged, and same-origin fetch("/api/…") calls run your route handlers in-process. Ctrl+C quits and restores your scrollback. It needs Bun.

Quit with q

Terminal users expect q. useApp().exit() unmounts the app and restores the terminal; in the browser it unmounts the console app.

app/layout.tsx+3tsx
Added: import { useApp } from "@textjs/app";export default function RootLayout({ children }: { children?: ReactNode }) {Added:   const app = useApp();  useInput((e) => {    if (e.key === "backspace") router.back();Added:     if (e.input === "q") app.exit();  });

Your own CLI

For a single component, use renderToTerminal. It mounts one element without the App Router, and fetch("/api/…") only reaches your route handlers under text run, so pass the component its data:

cli.tsxtsx
import { renderToTerminal } from "@textjs/app/terminal";import { Table } from "@textjs/app";import { DEPLOYMENTS } from "./lib/deployments";const columns = [{ key: "commit", header: "Commit" }, { key: "project", header: "Project", width: 10 }];const app = await renderToTerminal(<Table columns={columns} rows={DEPLOYMENTS} autoFocus />);await app.waitUntilExit();

Run it with bun cli.tsx. DeploymentsPage fetches its rows, so it needs text run.

What's different

Terminals repaint faster (up to 60fps instead of ~15) and size the grid to the window instead of the docked console. Images need pixel data instead of a URL. Everything else, including this dashboard, is the same code.

Try it

This is Acme Deploys at the end of the chapter, running in DevTools. Click the pad, then use the keys below.

▼ 09 · Running in a Terminal

Open the console

⌘⌥J
CtrlShiftJ
⌘⌥K
CtrlShiftK
⌘⌥C

The app renders in DevTools.First turn on “Show features for web developers” in Safari Settings → Advanced. Then click here and press Enter.

Open on a desktop

This app renders in the DevTools console, which needs a desktop browser.

Quiz

2 questions

Answer every question to complete the chapter.

What changes when the app runs in a terminal?

 

How does an app quit itself?