Skip to content

Karanjot786/TermUI

TermUI

Build terminal applications in TypeScript.

Documentation npm version License Bun 1.3+ 600 tests passing TypeScript 5.9

GSSoC 2026 Good first issues Contributors Stars

GSSoC 2026 contributors: star this repo before opening a PR. The star-check workflow blocks unstarred merges. Read CONTRIBUTING.md for the full point system.

📖 Docs site: API docs, guides, and examples live at termui.io. The source is at Karanjot786/TermUI_Docs.

What is TermUI?

TermUI is a TypeScript framework for building terminal apps. You get a layout engine, JSX support, React-style hooks, focus management, global state, theming, animations, routing, real-time data, error boundaries, and a hot-reload dev server. No curses bindings. No C extensions. Pure TypeScript.

Quick Start

bunx create-termui-app my-app
cd my-app
bun install
bun run dev

Requires Bun 1.3.0 or newer.

Manual Setup

bun add @termuijs/core @termuijs/widgets @termuijs/jsx
import { App } from '@termuijs/core'
import { Box, Text } from '@termuijs/widgets'
import { useState, useKeymap, ErrorBoundary } from '@termuijs/jsx'

function Counter() {
    const [count, setCount] = useState(0)

    useKeymap({
        '+':      () => setCount((c) => c + 1),
        'ctrl+c': () => process.exit(0),
    })

    return (
        <Box border="round" padding={1}>
            <Text bold>Count: {count}</Text>
            <Text dim>Press + to increment, ctrl+c to quit</Text>
        </Box>
    )
}

render(
    <ErrorBoundary fallback={(e) => <Text color="red">{e.message}</Text>}>
        <Counter />
    </ErrorBoundary>
)

Packages

Package What it does
@termuijs/core Screen buffer, input parsing, event system, flexbox layout, caps flags, WCAG utilities
@termuijs/widgets 40+ widgets: Box, Text, Table, VirtualList, StreamingText, ChatMessage, ToolCall, Card, Sidebar, LineChart, HeatMap, and more
@termuijs/ui Select, Tabs, Modal, Toast, NotificationCenter, imperative prompts, PasswordInput, NumberInput, PathInput
@termuijs/jsx JSX runtime with useState, useEffect, useKeymap, useMotion, ErrorBoundary, and a complete focus system
@termuijs/store Zustand-style global state with selector subscriptions and batch updates
@termuijs/testing In-memory test renderer: render, query, fireKey, waitFor, renderToString
@termuijs/tss Terminal Style Sheets with variables, pseudo-classes, AutoThemeProvider, and useTheme
@termuijs/motion Spring-physics and easing animations; respects NO_MOTION
@termuijs/router File-based screen routing with typed params, guards, and ErrorBoundary wrapping
@termuijs/data Real-time system data: CPU, memory, disk, network, processes; raw API and reactive hooks
@termuijs/dev-server Hot-reload dev server with graceful restart in under 200ms
@termuijs/quick Fluent builder API for dashboards in ~20 lines
create-termui-app Project scaffolding CLI

Features

useKeymap

Declare key bindings as a map. Cleaner than chained if-statements. Multiple calls in one component are additive.

import { useKeymap } from '@termuijs/jsx'

function App() {
    useKeymap({
        'ctrl+c': () => process.exit(0),
        'ctrl+s': () => save(),
        '/':      () => openSearch(),
        '?':      () => showHelp(),
    })
    return <Box>...</Box>
}

Focus management

Four hooks for building keyboard-accessible interfaces:

import { useFocusManager, useFocus, useFocusTrap, useKeyboardNavigation } from '@termuijs/jsx'

// App root: owns global focus state
function App() {
    const { FocusContext, focus, blur, focused } = useFocusManager()
    return (
        <FocusContext.Provider value={{ focus, blur, focused }}>
            <Screen />
        </FocusContext.Provider>
    )
}

// Modal: Tab key stays inside
function Modal({ fieldIds }) {
    useFocusTrap(fieldIds)
    return <Box>...</Box>
}

// List: standard arrow key navigation
function List({ items }) {
    const { selectedIndex } = useKeyboardNavigation({ items, loop: true })
    return <Box>...</Box>
}

ErrorBoundary

Wrap any subtree. Errors show a fallback instead of crashing the app.

import { ErrorBoundary } from '@termuijs/jsx'

<ErrorBoundary fallback={(err) => <Text color="red">Error: {err.message}</Text>}>
    <Dashboard />
</ErrorBoundary>

Capability flags

TermUI checks the terminal environment before rendering unicode or animations. Set environment variables to get ASCII output in CI or reduced-motion output for accessibility.

NO_UNICODE=1  # ASCII fallbacks for all widgets
NO_MOTION=1   # Skip all animations; static output
NO_COLOR=1    # Disable ANSI color sequences
import { caps } from '@termuijs/core'

const bullet = caps.unicode ? '●' : '*'

Notifications

import { NotificationCenter, useNotifications } from '@termuijs/ui'

function App() {
    return (
        <Box>
            <NotificationCenter position="top-right" />
            <Dashboard />
        </Box>
    )
}

function Dashboard() {
    const { notify } = useNotifications()

    useKeymap({
        's': async () => {
            await save()
            notify('Saved', { type: 'success', duration: 2000 })
        },
    })
    return <Box>...</Box>
}

Imperative prompts

Ask for user input without restructuring your component tree.

import { prompt } from '@termuijs/ui'

const ok = await prompt.confirm('Delete all logs?')
if (ok) deleteLogs()

const name = await prompt.text('Enter a name:')
const env = await prompt.select('Choose environment:', ['dev', 'staging', 'prod'])

Global state with batch updates

import { createStore, batch } from '@termuijs/store'

const useAppStore = createStore((set) => ({
    count: 0,
    label: '',
    increment: () => set((s) => ({ count: s.count + 1 })),
}))

// Multiple updates; one render pass
batch(() => {
    useAppStore.setState({ count: 10 })
    useAppStore.setState({ label: 'done' })
})

Theming

import { AutoThemeProvider, useTheme } from '@termuijs/tss'
import { tokensToTSS, nordTheme } from '@termuijs/tss'

// Detect terminal background and pick the closest theme
<AutoThemeProvider fallback="dracula">
    <App />
</AutoThemeProvider>

// Switch at runtime
const { theme, setTheme } = useTheme()
setTheme('catppuccin')

// Use token objects directly
const primary = nordTheme['--primary']

AI widgets

Display AI tool usage and streaming output with purpose-built widgets:

import { StreamingText, ChatMessage, ToolCall } from '@termuijs/widgets'

<ChatMessage role="assistant" content="Here is the result..." />
<ToolCall name="read_file" status="running" args={{ path: '/etc/hosts' }} />
<StreamingText text="Generating response..." speed={30} />

VirtualList

Renders only visible rows. A list of 1,000,000 items performs the same as a list of 10.

const list = new VirtualList({
    totalItems: 1_000_000,
    renderItem: (index) => `Log line ${index}`,
    onSelect: (index) => inspect(index),
})

Reactive system data

import { useCpu, useMemory, useHttpHealth } from '@termuijs/data'

function Dashboard() {
    const cpu  = useCpu(500)
    const mem  = useMemory(1000)
    const checks = useHttpHealth(['https://api.example.com/health'], 5000)

    return (
        <Box flexDirection="column">
            <Text>CPU: {cpu.usage.toFixed(1)}%</Text>
            <Text>MEM: {(mem.used / mem.total * 100).toFixed(1)}%</Text>
        </Box>
    )
}

Testing

import { render } from '@termuijs/testing'

const t = render(<Counter />)
t.fireKey('+')
expect(t.getByText('Count: 1')).toBeTruthy()

// Async assertions
await t.waitFor(() => {
    expect(t.getByText('Loaded')).toBeTruthy()
})

// ANSI-free snapshot
expect(t.renderToString()).toContain('Count: 1')
t.unmount()

Architecture

Application Layer:    @termuijs/ui · @termuijs/quick · create-termui-app
Component Layer:      @termuijs/widgets · @termuijs/jsx · @termuijs/store · @termuijs/testing
Feature Layer:        @termuijs/tss · @termuijs/router · @termuijs/motion
Core Layer:           @termuijs/core · @termuijs/data
Dev tooling:          @termuijs/dev-server

Every layer depends only on the layers below it. Use any package independently.

Examples

System dashboard (Quick API)

import { app, gauge, table } from '@termuijs/quick'
import { useCpu, useMemory } from '@termuijs/quick'

app('System Monitor')
    .rows(
        app.cols(
            gauge('CPU', () => useCpu().usage / 100),
            gauge('MEM', () => useMemory().percent / 100),
        ),
        table('Processes', {
            columns: ['Name', 'PID', 'CPU%'],
            data: () => processes.top(10).map((p) => ({
                Name: p.name,
                PID: p.pid,
                'CPU%': p.cpu.toFixed(1),
            })),
        }),
    )
    .run()

AI assistant dashboard

import { app, chatMessage, toolCall, streamingText } from '@termuijs/quick'

app('AI Assistant')
    .rows(
        chatMessage({ role: 'user', content: 'Show me the top processes' }),
        toolCall({ name: 'get_processes', status: 'running' }),
        streamingText({ text: 'Fetching system data...', speed: 40 }),
    )
    .run()

Running the examples

git clone https://github.com/Karanjot786/TermUI.git
cd TermUI
bun install
bun run build

cd examples/dashboard
bun run dev

Six examples: dashboard, jsx-dashboard, showcase, system-monitor, todo-app, widget-gallery.

Project structure

packages/
  core/              Screen buffer, input, events, layout, caps flags
  widgets/           Widget library (40+ widgets)
  ui/                High-level components and inputs
  jsx/               JSX runtime, hooks, focus system, ErrorBoundary
  store/             Global state management
  testing/           Test renderer
  tss/               Terminal Style Sheets and theming
  motion/            Spring animation engine
  router/            File-based routing
  data/              System data providers and hooks
  dev-server/        Hot-reload dev server
  quick/             Fluent builder API
  create-termui-app/ Project scaffolding CLI
examples/
  dashboard/         Real-time system monitor
  jsx-dashboard/     JSX-based dashboard
  showcase/          Widget gallery
  system-monitor/    Advanced monitor
  todo-app/          Interactive todo list
  widget-gallery/    All widgets in one place

Development

bun install
bun run build      # Build all 13 packages
bun run test       # Run all 600 tests
bun run typecheck  # Type-check all packages

Requirements

  • Bun 1.3.0 or newer for development
  • Node.js 18+ to run published @termuijs/* packages from npm
  • A terminal with TTY support (256-color or truecolor recommended)

License

MIT

About

TermUI is a TypeScript/JavaScript framework for building terminal apps. Flexbox layout, JSX, hooks, global state, theming, animations, routing, and hot reload. No native dependencies.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors