Getting started

OpenTUI is a native terminal UI core written in Zig with TypeScript bindings. The native core exposes a C ABI and can be used from any language. OpenTUI powers OpenCode in production today and will also power terminal.shop. It is an extensible core with a focus on correctness, stability, and high performance. It provides a component-based architecture with flexible layout capabilities, allowing you to create complex terminal applications.

Installation

OpenTUI’s renderer examples use Bun. You can import portable entry points such as @opentui/keymap and @opentui/core in Node.js without installing Bun or enabling FFI. These imports do not create a native renderer. To create a native renderer in Node.js, you need Node.js 26.4.0 with experimental FFI enabled.

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

Runtime support

Importing packages does not require native FFI. For example, Node.js can load await import("@opentui/keymap") and await import("@opentui/core") without --experimental-ffi.

Creating a native renderer requires FFI. This includes calling createCliRenderer() or any other API that loads and calls OpenTUI’s native Zig renderer. In Node.js, use Node.js 26.4.0 with --experimental-ffi. If you use Node.js permissions, you must also add --allow-ffi and the filesystem permissions your app needs. OpenTUI does not install Node.js, so select the required version before you run Node.js-specific examples or scripts.

Hello world

Create index.ts:

import { createCliRenderer, Text } from "@opentui/core"

const renderer = await createCliRenderer({
  exitOnCtrlC: true,
})

renderer.root.add(
  Text({
    content: "Hello, OpenTUI!",
    fg: "#00FF00",
  }),
)

Run it:

bun index.ts

You should see green text. Press Ctrl+C to exit.

Composing components

Components nest naturally. Here’s a bordered panel with content:

import { createCliRenderer, Box, Text } from "@opentui/core"

const renderer = await createCliRenderer({
  exitOnCtrlC: true,
})

renderer.root.add(
  Box(
    { borderStyle: "rounded", padding: 1, flexDirection: "column", gap: 1 },
    Text({ content: "Welcome", fg: "#FFFF00" }),
    Text({ content: "Press Ctrl+C to exit" }),
  ),
)

Box and Text are factory functions. The first argument is props; additional arguments are children.

What’s next

Core concepts

Components

Framework bindings

The OpenTUI monorepo includes @opentui/react and @opentui/solid. Other language and framework bindings live in separate repositories.