All skills
msmps avatar

/opentui

@0d61d6d
by Matt Simpsonmsmps/opentui-skill225 stars
9

OpenTUI skill for building terminal user interfaces with the Core, React, or Solid APIs. Use for any TUI task including components, layout, keyboard and keymap handling, animations, and testing.

Use this Skill: https://skilld.dev/gh/msmps/opentui-skill/opentui

This session only. Nothing lands on disk.

referencescomponentstext-display.md

≈2.8k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Text & Display Components

Components for displaying text content in OpenTUI.

Text Component

The primary component for displaying styled text.

Basic Usage

// React/Solid
<text>Hello, World!</text>

// With content prop
<text content="Hello, World!" />

// Core
const text = new TextRenderable(renderer, {
  id: "greeting",
  content: "Hello, World!",
})

Styling (React/Solid)

For React and Solid, use nested modifier tags for text styling:

<text fg="#FFFFFF" bg="#000000">
  <strong>Bold</strong>, <em>italic</em>, and <u>underlined</u>
</text>

Important: Do NOT use bold, italic, underline, dim, strikethrough as props on <text> — they don't work. Always use nested tags like <strong>, <em>, <u>, or <span> with styling.

Styling (Core) - Text Attributes

import { TextRenderable, TextAttributes } from "@opentui/core"

const text = new TextRenderable(renderer, {
  content: "Styled",
  attributes: TextAttributes.BOLD | TextAttributes.UNDERLINE,
})

Available attributes:

  • TextAttributes.BOLD
  • TextAttributes.DIM
  • TextAttributes.ITALIC
  • TextAttributes.UNDERLINE
  • TextAttributes.BLINK
  • TextAttributes.INVERSE
  • TextAttributes.HIDDEN
  • TextAttributes.STRIKETHROUGH

Text Selection

<text selectable>
  This text can be selected by the user
</text>

<text selectable={false}>
  This text cannot be selected
</text>

For copy-on-selection and the full selection API, see keyboard/REFERENCE.md (selection).

Text Modifiers

Inline styling elements that must be used inside <text>:

Span

Inline styled text:

<text>
  Normal text with <span fg="red">red text</span> inline
</text>

Bold/Strong

<text>
  <strong>Bold text</strong>
  <b>Also bold</b>
</text>

Italic/Emphasis

<text>
  <em>Italic text</em>
  <i>Also italic</i>
</text>

Underline

<text>
  <u>Underlined text</u>
</text>

Line Break

<text>
  Line one
  <br />
  Line two
</text>

Link

<text>
  Visit <a href="https://example.com">our website</a>
</text>

Combined Modifiers

<text>
  <span fg="#00FF00">
    <strong>Bold green</strong>
  </span>
  and
  <span fg="#FF0000">
    <em><u>italic underlined red</u></em>
  </span>
</text>

Styled Text Template (Core)

The t template literal for complex styling:

import { t, bold, italic, underline, fg, bg, dim } from "@opentui/core"

const styled = t`
  ${bold("Bold")} and ${italic("italic")} text.
  ${fg("#FF0000")("Red text")} with ${bg("#0000FF")("blue background")}.
  ${dim("Dimmed")} and ${underline("underlined")}.
`

const text = new TextRenderable(renderer, {
  content: styled,
})

Style Functions

Function Description
bold(text) Bold text
italic(text) Italic text
underline(text) Underlined text
dim(text) Dimmed text
strikethrough(text) Strikethrough text
fg(color)(text) Set foreground color
bg(color)(text) Set background color

ASCII Font Component

Display large ASCII art text banners.

Basic Usage

// React
<ascii-font text="TITLE" font="tiny" />

// Solid
<ascii_font text="TITLE" font="tiny" />

// Core
const title = new ASCIIFontRenderable(renderer, {
  id: "title",
  text: "TITLE",
  font: "tiny",
})

Available Fonts

Font Description
tiny Compact ASCII font
block Block-style letters
slick Sleek modern style
shade Shaded 3D effect
huge Large font
grid Grid-style font
pallet Pallet-style font

Styling

// React
<ascii-font
  text="HELLO"
  font="block"
  color="#00FF00"
/>

// Core
import { RGBA } from "@opentui/core"

const title = new ASCIIFontRenderable(renderer, {
  text: "HELLO",
  font: "block",
  color: RGBA.fromHex("#00FF00"),
})

Example Output

Font: tiny
╭─╮╭─╮╭─╮╭╮╭╮╭─╮╶╮╶ ╶╮
│ ││─┘├┤ │╰╯││  │  │
╰─╯╵  ╰─╯╵  ╵╰─╯╶╯╶╰─╯

Font: block
█▀▀█ █▀▀█ █▀▀ █▀▀▄
█  █ █▀▀▀ █▀▀ █  █
▀▀▀▀ ▀    ▀▀▀ ▀  ▀

Image Component

Display PNG, JPEG, WebP, GIF, or raw image data. OpenTUI chooses Kitty, Sixel, or Unicode block rendering based on terminal capabilities.

// React and Solid
<image source="./cover.webp" fit="cover" protocol="auto" width={40} height={15} />

// Core
const image = new ImageRenderable(renderer, {
  source: "./cover.webp",
  width: 40,
  height: 15,
  fit: "cover",
  protocol: "auto",
  onError: console.error,
})
renderer.root.add(image)
await image.loadPromise

source accepts a path, supported URL, URL, Blob, Response, Uint8Array, ArrayBuffer, or NativeImage. Replacing source keeps the current image visible until the replacement succeeds.

Option Values Description
fit fit, cover, fill Contain (default), crop, or stretch
protocol auto, kitty, sixel, blocks Requested terminal rendering protocol
onLoad (image: NativeImage) => void Current source loaded
onError (error: unknown) => void Current source failed

State includes image, loading, loadError, loadPromise, effectiveProtocol, and getFittedSize(). Set OPENTUI_IMAGE_PROTOCOL=auto|kitty|sixel|blocks for a global default; OPENTUI_GRAPHICS=false disables Kitty and Sixel detection.

NativeImage

Use NativeImage when you need to inspect, transform, or share decoded pixels:

import { NativeImage, imageInfo } from "@opentui/core"

const source = await NativeImage.load("photo.jpg")
const thumbnail = source.resize({ width: 320 })
const shared = thumbnail.retain() // Independent handle, no pixel copy

try {
  console.log(imageInfo(await Bun.file("photo.jpg").arrayBuffer()))
  const rgba = thumbnail.raw("rgba8")
} finally {
  shared.dispose()
  thumbnail.dispose()
  source.dispose()
}

Creation methods are load(), decode(), and fromRgba(). Pixel methods are raw(), copyTo(), and ownership-transferring takeRaw(). Transform methods include resize(), extract(), extend(), rotate(), flip(), flop(), and composite(). retain() shares storage, clone() copies it, and every returned native handle must be disposed separately. ensureEncodedPng() prepares an encoded PNG for low-level native consumers. ImageRenderable retains a supplied NativeImage, so the caller still owns and must dispose its source reference.

Time to First Draw

TimeToFirstDrawRenderable is a rendering diagnostic. It captures performance.now() on its first draw; this is a runtime-relative timestamp, not elapsed application startup time.

// React
<time-to-first-draw label="First draw timestamp" precision={1} />

// Solid
<time_to_first_draw label="First draw timestamp" precision={1} />

// Core
const firstDraw = new TimeToFirstDrawRenderable(renderer, {
  label: "First draw timestamp",
  precision: 1,
})
renderer.root.add(firstDraw)

React and Solid also export a TimeToFirstDraw wrapper. runtimeMs is null before the first draw; call reset() to capture again. Keep precision an integer from 0 through 100.

QR Code Component

Render a QR code in the terminal. Ships as a separate package, @opentui/qrcode (not part of @opentui/core).

bun add @opentui/qrcode
// Core
import { createCliRenderer } from "@opentui/core"
import { QRCodeRenderable } from "@opentui/qrcode"

const renderer = await createCliRenderer()
const qr = new QRCodeRenderable(renderer, {
  id: "docs-link",
  content: "https://opentui.com/docs/getting-started",
  quietZone: 4,
  scale: 2,
})
renderer.root.add(qr)

React and Solid require explicit element registration (the elements are not built in):

// React — element <qr-code>
import { registerQRCode } from "@opentui/qrcode/react"
registerQRCode()
<qr-code content="https://opentui.com" quietZone={4} scale={2} />

// Solid — element <qr_code> (underscore)
import { registerQRCode } from "@opentui/qrcode/solid"
registerQRCode()
<qr_code content="https://opentui.com" quietZone={4} scale={2} />
Prop Type Default Notes
content string "" Text/URL to encode
errorCorrectionLevel ErrorCorrectionLevel M .L / .M / .Q / .H
quietZone number 4 Must be ≥ 4 (throws otherwise)
scale number 1 Columns per module before fitting
fit "contain" | "none" "contain" contain shrinks to parent
foregroundColor ColorInput "#000000" Dark module color
backgroundColor ColorInput "#ffffff" Light module / quiet-zone color
fallbackContent string "" Shown when too small to render
fallbackColor ColorInput "#ffffff" Fallback text color

Import ErrorCorrectionLevel from @opentui/qrcode, e.g. errorCorrectionLevel: ErrorCorrectionLevel.H. Read-only getters: version, moduleCount.

Colors

Color Formats

// Hex colors
<text fg="#FF0000">Red</text>
<text fg="#F00">Short hex</text>

// Named colors
<text fg="red">Red</text>
<text fg="blue">Blue</text>

// Transparent
<text bg="transparent">No background</text>

RGBA Class

Color props accept string formats ("#FF0000", "#F00", "red", "transparent") in all frameworks. For programmatic color manipulation, the RGBA class from @opentui/core (fromHex / fromInts / fromValues / parseColor) works in Core, React, and Solid alike:

import { RGBA } from "@opentui/core"

<box backgroundColor={RGBA.fromHex("#1a1a2e")} borderColor={RGBA.fromInts(122, 162, 247, 255)}>
  <text fg={RGBA.fromHex("#c0caf5")}>Styled with RGBA</text>
</box>

See core/api.md → Colors (RGBA) for the full constructor reference and the "when to use each method" guidance.

Text Wrapping

Text wraps based on parent container:

<box width={40}>
  <text>
    This long text will wrap when it reaches the edge of the 
    40-character wide parent container.
  </text>
</box>

Dynamic Content

React

function Counter() {
  const [count, setCount] = useState(0)
  return <text>Count: {count}</text>
}

Solid

function Counter() {
  const [count, setCount] = createSignal(0)
  return <text>Count: {count()}</text>
}

Core

const text = new TextRenderable(renderer, {
  id: "counter",
  content: "Count: 0",
})

// Update later
text.setContent("Count: 1")

Gotchas

Text Modifiers Outside Text

// WRONG - modifiers only work inside <text>
<box>
  <strong>Won't work</strong>
</box>

// CORRECT
<box>
  <text>
    <strong>This works</strong>
  </text>
</box>

Empty Text

// May cause layout issues
<text></text>

// Better - use space or conditional
<text>{content || " "}</text>

Color Format

// WRONG
<text fg="FF0000">Missing #</text>

// CORRECT
<text fg="#FF0000">With #</text>

Source: SKILL.md on GitHub

No alerts17d5 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    This skill contains documentation and usage patterns for the OpenTUI framework, designed for building terminal user interfaces across Core, React, and SolidJS. The analysis confirmed that the skill provides legitimate development guidance; no malicious code, persistence mechanisms, or obfuscation were detected. Findings are limited to standard platform capabilities and recommended scaffolding tools.

  • Socket17d

    No alerts

  • Snyk17d

    Risk: LOW · No issues

  • Runlayer7mo

    1/26 files flagged

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 0d61d6d. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated last month
Other metadata
metadata
{
  "references": "core, react, solid, components, layout, keyboard, keymap, animation, testing"
}
  • React
  • Testing
  • terminal-ui
  • tui
  • solid
  • components
  • keyboard-input
  • animations
  • layout

README badge

README badge for msmps/opentui-skill

Builds terminal user interfaces with OpenTUI using an imperative core API or React/Solid reconcilers. Covers layout, keyboard handling, animations, components, and testing for full-featured TUI applications.

Generated from the current SKILL.md.

Does this skill cover all three OpenTUI frameworks?
Yes. The skill consolidates the core imperative API, React reconciler, and Solid reconciler. Use the decision tree in the skill to pick the right framework for your task.
How do I start a new OpenTUI project?
Use `create-tui` with options before arguments: `bunx create-tui -t react my-app` (not `bunx create-tui my-app -t react`). Then read the REFERENCE.md for your chosen framework.
What runtime does OpenTUI require?
OpenTUI runs on Bun and uses Zig for native builds. See `./references/core/gotchas.md` for runtime requirements and build guidance.
Can I use `process.exit()` to shut down my TUI?
No. Always use `renderer.destroy()` instead. Calling `process.exit()` directly is a common pitfall covered in `core/gotchas.md`.
How do I apply text styling in React or Solid?
Use nested modifier elements, not props. See `components/text-display.md` for the correct pattern.

Generated from the current SKILL.md. These answers refresh after source changes.