opentui

Build terminal UIs with OpenTUI. Covers Core, frameworks, components, application APIs, testing, extensions, integrations, deployment, and public API lookup.

Install
npx skills add 'https://github.com/anomalyco/opentui/tree/main/packages/web/src/content'
Download bundle ↓
main · ac753b4Scanned 2026-09-15

Contributors

GitHub-linked commit authors for this SKILL.md at the saved revision. Co-authors and history before file renames are not included.

File history ↗

docs/components/input.mdx

docs/components/input.mdxBrowse 89 files
View on GitHub
← Back to SKILL.md

title: Input description: Accept one line of text with cursor, focus, and submit events skill: entry: true intents: [input, form, editing, focus]

Input

Input edits one line of text and supplies a cursor, placeholder, and focus styles. Use Textarea for multi-line editing.

Focus the input to receive keyboard input.

Availability

FieldAvailability
Package@opentui/core
Core renderableInputRenderable
React<input> (automatic)
Solid<input> (automatic)
StatusBuilt in

Basic usage

Renderable API

import { InputRenderable, InputRenderableEvents, createCliRenderer } from "@opentui/core"

const renderer = await createCliRenderer()

const input = new InputRenderable(renderer, {
  id: "name-input",
  width: 25,
  placeholder: "Enter your name...",
})

input.on(InputRenderableEvents.CHANGE, (value) => {
  console.log("Input value:", value)
})

input.focus()
renderer.root.add(input)

An empty input displays its placeholder:

Name
Enter your name

Focus states

The input changes appearance when focused:

const input = new InputRenderable(renderer, {
  id: "styled-input",
  width: 30,
  placeholder: "Type here...",
  backgroundColor: "#1a1a1a",
  focusedBackgroundColor: "#2a2a2a",
  textColor: "#FFFFFF",
  cursorColor: "#00FF00",
})

After focusing the input and typing a name:

Name
Ada Lovelace

Events

Input event

Emitted after text insertion and deletion operations, and when assigning a different value. The listener receives the current value:

import { InputRenderableEvents } from "@opentui/core"

input.on(InputRenderableEvents.INPUT, (value: string) => {
  console.log("Current value:", value)
})

Change event

Fires on blur or after a successful submit() when the value differs from the last commit baseline:

input.on(InputRenderableEvents.CHANGE, (value: string) => {
  console.log("Value committed:", value)
})

Each focus() call sets the baseline. Each emitted CHANGE event updates it. Thus, blur does not emit another event if the value did not change after a successful commit.

Enter event

Emitted when an Enter/Return submit succeeds. The input emits no event when the current UTF-16 code-unit length is less than minLength:

input.on(InputRenderableEvents.ENTER, (value: string) => {
  console.log("Submitted value:", value)
})

Getting the current value

const currentValue = input.value

Setting the value

input.value = "New value"

Properties

PropertyTypeDefaultDescription
widthnumber, "auto", or percentage string"auto"Input field width
valuestring""Initial text value
placeholderstring""Placeholder text when empty
minLengthnumber0Minimum UTF-16 code-unit length required for submit
maxLengthnumber1000Maximum UTF-16 code-unit length
backgroundColorstring | RGBA"transparent"Background when unfocused
focusedBackgroundColorstring | RGBAbackgroundColor, else "transparent"Background when focused
textColorstring | RGBA"#FFFFFF"Text color
cursorColorstring | RGBA"#FFFFFF"Cursor color
position"relative" | "absolute""relative"Effective positioning mode

Example: login form

Input has no password-masking mode. This demonstration displays the password value as normal terminal text. Do not use this pattern for real secrets.

import { BoxRenderable, InputRenderable, TextRenderable, createCliRenderer } from "@opentui/core"

const renderer = await createCliRenderer()
const form = new BoxRenderable(renderer, {
  width: 40,
  borderStyle: "rounded",
  title: "Login",
  padding: 1,
  gap: 1,
})
const usernameInput = new InputRenderable(renderer, {
  id: "username-input",
  placeholder: "Enter username",
  width: 20,
  backgroundColor: "#222",
  focusedBackgroundColor: "#333",
})
const passwordInput = new InputRenderable(renderer, {
  id: "password-input",
  placeholder: "Enter password",
  width: 20,
  backgroundColor: "#222",
  focusedBackgroundColor: "#333",
})

form.add(new TextRenderable(renderer, { content: "Username:", fg: "#888888" }))
form.add(usernameInput)
form.add(new TextRenderable(renderer, { content: "Password:", fg: "#888888" }))
form.add(passwordInput)
renderer.root.add(form)
usernameInput.focus()

Tab navigation

Add tab navigation between inputs:

const inputs = [usernameInput, passwordInput]
let focusIndex = 0

renderer.keyInput.on("keypress", (key) => {
  if (key.name === "tab") {
    focusIndex = (focusIndex + 1) % inputs.length
    inputs[focusIndex].focus()
  }
})

Read Interaction, focus, and selection for focus and event behavior. Read Text and terminal cells before enforcing limits that depend on code points, graphemes, or display cells.

Referenced from SKILL.md