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

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#

Property Type Default Description
width number, "auto", or percentage string "auto" Input field width
value string "" Initial text value
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" 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.