Quickstart

Build a small counter controlled by the arrow keys. The finished app uses layout, text, keyboard input, live updates, and explicit cleanup.

Prerequisite#

This guide uses Bun to run TypeScript source. See Runtime and platform support for other runtime and platform requirements.

Create an empty project and add OpenTUI:

mkdir my-tui && cd my-tui
bun init -y
bun add @opentui/core

Create the app#

Create index.ts:

import { BoxRenderable, TextRenderable, createCliRenderer } from "@opentui/core"

const renderer = await createCliRenderer({
  exitOnCtrlC: true,
  backgroundColor: "#1131E9",
})

let count = 0
const counter = new TextRenderable(renderer, {
  id: "counter",
  content: "Count  0",
  fg: "#FFFFFF",
})

const panel = new BoxRenderable(renderer, {
  width: 42,
  height: 9,
  backgroundColor: "#1131E9",
  alignItems: "center",
  justifyContent: "center",
})
const content = new BoxRenderable(renderer, {
  width: 38,
  height: 7,
  backgroundColor: "#2947F0",
  padding: 1,
  flexDirection: "column",
  gap: 1,
  alignItems: "center",
})

content.add(new TextRenderable(renderer, { content: "Hello, OpenTUI!", fg: "#DCE3FF" }))
content.add(counter)
content.add(new TextRenderable(renderer, { content: "left/right change | q quit", fg: "#AEBBFF" }))
panel.add(content)
renderer.root.add(panel)

renderer.keyInput.on("keypress", (key) => {
  if (key.name === "q") {
    renderer.destroy()
    return
  }

  if (key.name === "left") count--
  else if (key.name === "right") count++
  else return

  counter.content = `Count  ${count}`
})

createCliRenderer() takes control of the terminal and returns a CliRenderer. Its root property is the root of the component tree. BoxRenderable and TextRenderable are imperative tree nodes. The event handler updates the counter’s content property directly.

The renderer emits parsed keyboard events through keyInput. The handler changes counter.content, which schedules a new frame.

Run it#

bun index.ts

Press the Left and Right arrow keys. The count updates in place.

The application creates and owns the renderer. It must call renderer.destroy() on every shutdown path to release resources and restore the terminal. This example calls it when you press q. exitOnCtrlC: true handles the Ctrl+C path.

Continue#