React renderer for interactive terminal UIs in TypeScript
Why Glyph • Quick Start • Getting Started • Examples • Comparison • Docs
Glyph is a React renderer for the terminal. It handles layout, focus, keyboard input, and ships a full component library — write TUI apps the same way you write web apps.
A dashboard built entirely with Glyph components
Built for full-screen, keyboard-driven applications — dashboards, editors, form wizards, dev tools. Not for simple CLI output or argument parsing.
Everything is declarative: you describe layout and focus order, Glyph handles rendering and navigation.
- Flexbox layout — Yoga handles rows, columns, wrapping, alignment, gaps, padding. No coordinate math.
- Focus system — Tab navigation, focus scopes, modal trapping, JumpNav (vim-style quick-jump hints).
- 20+ components — Input, Button, Select, Checkbox, Radio, ScrollView, List, Menu, Progress, Spinner, Image, Toasts, Dialogs, Portal, and more.
- Character-level diffing — Double-buffered framebuffer. Only changed cells hit stdout.
- TypeScript-first — Every prop, style, and hook is typed.
How it compares: Ink uses React + Yoga but ships ~4 components with limited focus. Blessed has widgets but is imperative, unmaintained, and JS-only. Textual (Python) and Bubbletea (Go) serve their ecosystems well; Glyph is the equivalent for TypeScript/React. Full comparison →
Scaffold a new project:
bun create @semos-labs/glyph my-app # or: npm / pnpm / yarn create
cd my-app && bun install && bun devOr add to an existing project:
bun add @semos-labs/glyph react
# or: npm install / pnpm addCreate app.tsx:
import React from "react";
import { render, Box, Text, Keybind, useApp } from "@semos-labs/glyph";
function App() {
const { exit } = useApp();
return (
<Box style={{ border: "round", borderColor: "cyan", padding: 1 }}>
<Text style={{ bold: true, color: "green" }}>Hello, Glyph!</Text>
<Keybind keypress="q" onPress={() => exit()} />
</Box>
);
}
render(<App />);npx tsx app.tsxA rounded cyan box with bold green text appears. Press q to exit.
A form with flexbox layout, text input, a dropdown, and keyboard navigation:
import React, { useState } from "react";
import { render, Box, Text, Input, Select, Button, Keybind, useApp } from "@semos-labs/glyph";
function App() {
const { exit } = useApp();
const [name, setName] = useState("");
const [lang, setLang] = useState<string | undefined>();
return (
<Box style={{ flexDirection: "column", gap: 1, padding: 1, border: "round", borderColor: "cyan" }}>
<Text style={{ bold: true }}>New Project</Text>
<Input value={name} onChange={setName} placeholder="Project name..." />
<Select
items={[
{ label: "TypeScript", value: "ts" },
{ label: "JavaScript", value: "js" },
{ label: "Go", value: "go" },
]}
value={lang}
onChange={setLang}
placeholder="Language"
/>
<Box style={{ flexDirection: "row", gap: 2 }}>
<Button onPress={() => { /* create project */ }}>
<Text>Create</Text>
</Button>
<Button onPress={() => exit()}>
<Text style={{ dim: true }}>Cancel</Text>
</Button>
</Box>
<Keybind keypress="escape" onPress={() => exit()} />
</Box>
);
}
render(<App />);Tab between fields, type to filter the select, Enter to submit. Focus order is automatic.
What Glyph ships:
Layout & Text — <Box>, <Text>, <Spacer>
Form Controls — <Input>, <Button>, <Checkbox>, <Radio>, <Select>
Lists & Menus — <List>, <Menu>, <ScrollView> (with virtualization)
Overlays — <Portal>, <FocusScope>, <ToastHost>, <DialogHost>
Utilities — <Keybind>, <JumpNav>, <Progress>, <Spinner>, <Image>
Hooks — useInput, useFocus, useFocusable, useLayout, useApp, useToast, useDialog
Styling uses a style prop with CSS-like flexbox properties: flexDirection, gap, padding, border, bg, color, bold, and more. Colors accept named values, hex, RGB, and 256-palette with auto-contrast on colored backgrounds.
Full API reference and guides: semos.sh/docs/glyph
| Example | Description | Source |
|---|---|---|
| dashboard | Full task manager using all components | View → |
| modal-input | Modal dialogs with focus trapping | View → |
| jump-nav | Vim-style keyboard hints for navigation | View → |
| forms-demo | Checkbox, Radio, and form controls | View → |
| virtualized-list | ScrollView with 10k+ items | View → |
| showcase | Progress bars, Spinners, Toasts | View → |
# Run locally
git clone https://github.com/semos-labs/glyph.git && cd glyph
bun install && bun run build
bun run --filter dashboard dev|
Aion
Calendar & time management TUI |
Epist
Gmail client for your terminal |
Using Glyph in your project? Let us know!
Side-by-side with other TUI frameworks:
| Glyph | Ink | Blessed | Textual | Bubbletea | |
|---|---|---|---|---|---|
| Language | TypeScript | TypeScript | JavaScript | Python | Go |
| Paradigm | React (JSX) | React (JSX) | Imperative | Declarative (Python) | Elm architecture |
| Layout | Yoga flexbox | Yoga flexbox | Custom grid | CSS subset | Manual (lipgloss) |
| Built-in components | 20+ | ~4 (Box, Text, Spacer, Newline) | 30+ (widgets) | 30+ (widgets) | BYO (bubbles library) |
| Input, Select, Checkbox, … | ✅ Built-in | ❌ Community packages | ✅ Built-in | ✅ Built-in | ❌ Separate library |
| Focus system | ✅ Tab, scopes, trapping | ✅ Full | ❌ Manual | ||
| JumpNav (vim-hints) | ✅ | ❌ | ❌ | ❌ | ❌ |
| Rendering | Character-level diffing | Full re-render | Full re-render | Dirty widget re-render | Full re-render |
| Framebuffer | ✅ Double-buffered | ❌ | ❌ | ❌ | ❌ |
| True color (hex, RGB) | ✅ + auto-contrast | Via chalk | Partial | ✅ | Via lipgloss |
| Image support | ✅ Kitty/iTerm2 | ❌ | ❌ | ❌ | ❌ |
| Toasts & Dialogs | ✅ Built-in | ❌ | ❌ | ✅ | ❌ |
| Borders | 4 styles | ❌ (ink-box) | ✅ | ✅ | Via lipgloss |
| Maintained | ✅ Active | ❌ Abandoned | ✅ Active | ✅ Active |
Render pipeline: React reconciler → GlyphNode tree → Yoga flexbox layout → framebuffer rasterization → character-level diff → stdout.
Source: reconciler/, layout/, paint/, runtime/, components/, hooks/, render.ts. See docs for internals.
MIT
Built with React • Yoga • a lot of ANSI escape codes
