Inspector Textarea

About this component

Inspector Textarea

The family's block row: the same surface and tones, but the label sits above a multi-line field whose height comes from the text, with a character count whenever there is a limit to see. Collapsible, so a paragraph written once can be folded down to the height of a row without leaving the form.

Usage

The import and the props worth knowing about, in one place.

import { InspectorTextarea } from "@/components/beste/component/inspector-textarea";

// Uncontrolled. The whole block is a label, so a press anywhere puts the caret in.
<InspectorTextarea label="Description" defaultValue="A short line about the section." />

// Controlled, with a separate commit for expensive work
<InspectorTextarea
  label="Alt text"
  value={alt}
  onValueChange={setAlt}                                // every keystroke
  onValueCommit={(value) => console.log("persist", value)} // on blur, if it changed
  placeholder="Describe the image"
  rows={4}
/>

// A limit brings a count with it, since a limit nobody can see is a trap
<InspectorTextarea label="Summary" maxLength={160} value={summary} onValueChange={setSummary} />

<InspectorTextarea
  label="Custom CSS"
  icon={Code}            // optional leading icon
  rows={6}
  fixedHeight            // stay at `rows` and scroll, rather than growing
  tone="outline"         // "muted" (default) | "outline" | "ghost"
  size="sm"              // padding preset; the height comes from `rows`
  value={css}
  onValueChange={setCss}
/>

// Read-only, and taking part in a form
<InspectorTextarea label="Notes" name="notes" value={notes} onValueChange={setNotes} />
<InspectorTextarea label="Generated" value={output} readOnly />

// Folded away when it is not being written. Closed, the block measures exactly what
// a row of the family measures, and any character count stays in the header.
<InspectorTextarea label="Description" collapsible defaultOpen={false} onOpenChange={(open) => console.log("open:", open)} />

Props

Read from the component's own type, so this cannot drift from what it accepts.

PropTypeDefaultDescription
label*string—Label rendered above the field, inside the block.
iconLucideIcon—Optional leading icon shown before the label.
valuestring—Controlled value. Pair it with `onValueChange`.
defaultValuestring—Initial value in uncontrolled mode.
onValueChange(value: string) => void—Fires on every keystroke.
onValueCommit(value: string) => void—Fires once the edit is finished — leaving the field — and only when the value actually changed. Use it for work too expensive to run per keystroke.
placeholderstring—Ghost text shown while the field is empty.
rowsnumber3Lines the field opens at. It grows past this as text is typed, where the browser supports sizing a field to its content.
fixedHeightboolean—Keep the field at `rows` and scroll instead of growing.
collapsibleboolean—Let the block be folded away, leaving its header. A paragraph is several rows tall and usually written once, so the panel around it is otherwise mostly a scroll past something nobody is editing.
openboolean—Controlled open state. Pair it with `onOpenChange`.
defaultOpenbooleantrueWhether it starts open in uncontrolled mode.
onOpenChange(open: boolean) => void—
maxLengthnumber—Longest the value may be. Setting it also puts a count beside the label, which is the only reason a reader can tell how much room is left.
recommendedLengthnumber—The length the text reads best at. Shown as a count beside the label, which turns to a warning once it is passed. Advice, not a limit: unlike `maxLength` nothing stops at the number, because the cases this is for — a meta title, a share description — are ones where going long costs you something later rather than being impossible now.
insetbooleanfalseGive the field a surface of its own inside the block. Bare, the text sits directly on the block and a long value reads as the panel's own prose rather than as something you typed. Inset, it is plainly a field: somewhere to write, with an edge and room inside it.
singleLineboolean—Refuse line breaks. Enter does nothing and a pasted newline folds into a space. The field still grows as the text wraps, so a long value gets taller — it just never gets a second paragraph. For the one-line things that want this block rather than a row: a page title, a subject line, anything whose value is one sentence but too long to sit at the right of a row.
disabledboolean—Block interaction and dim the block.
readOnlyboolean—Show the value but refuse edits.
tone"muted" | "outline" | "ghost""muted"Surface treatment: filled (default), hairline outline, or bare until focus.
size"sm" | "default" | "lg""default"Padding preset.
namestring—Name of the field, so the block can take part in a form.
idstring—Id of the field. One is generated when it is not given.
classNamestring—
aria-labelstring—Accessible name. Falls back to the visible label.

More Inspector components

View all Inspector
landing pagesaaspricing