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

OpenTUI preview

Run this example to open an interactive terminal.

Source code · typescript
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 uses its focused colors while it has focus:

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 these operations:

  • Typing, paste, or insertText() inserts text.

  • A delete key, a delete method such as deleteCharBackward(), undo(), or redo() runs.

  • You assign a different value.

The listener receives the current value. Other buffer methods, such as setText(), insertChar(), deleteSelection(), and deleteRange(), do not emit it:

import { InputRenderableEvents } from "@opentui/core"

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

Change event

Emitted 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)
})

The constructor and each focus() call set 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 submit() succeeds. Return, keypad Enter, and linefeed call submit(). If the current UTF-16 code-unit length is less than minLength, submit() fails and the input emits no event:

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, with newlines removed
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"Positioning mode

Example: login form

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

OpenTUI preview

Run this example to open an interactive terminal.

Source code · typescript
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.

Changes0.7.0
0.7.0
core: An Input with wrap off limits its horizontal scroll by the widest line, not the widest text piece, so it no longer jumps sideways on resize. A typed character lands on the same side of zero-width text as in the same text loaded in one piece. (#1747)