These docs describe the main branch, including unreleased changes. Read the 0.6.0 docs.

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#

Field Availability
Package @opentui/core
Core renderable InputRenderable
React <input> (automatic)
Solid <input> (automatic)
Status Built 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:

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:

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#

Property Type Default Description
width number, "auto", or percentage string "auto" Input field width
value string "" Initial text, with newlines removed
placeholder string "" Placeholder text when empty
minLength number 0 Minimum UTF-16 code-unit length required for submit
maxLength number 1000 Maximum UTF-16 code-unit length
backgroundColor string | RGBA "transparent" Background when unfocused
focusedBackgroundColor string | RGBA backgroundColor, else "transparent" Background when focused
textColor string | RGBA "#FFFFFF" Text color
cursorColor string | 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.

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.6.0
0.6.0
Removed InputRenderable.handlePaste.