@semos-labs/glyph
v0.2.10
Published
A React renderer for terminal UIs with flexbox layout
Downloads
414
Maintainers
Readme
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.
Why Glyph
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 →
Quick Start
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 addHello World
Create 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.
Getting Started
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.
Components and Hooks
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
Examples
| 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 devBuilt with Glyph
Using Glyph in your project? Let us know!
Comparison
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 | ⚠️ Basic | ⚠️ Basic | ✅ 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 | ⚠️ Slow | ❌ Abandoned | ✅ Active | ✅ Active |
Architecture
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.
License
MIT
