UI componentsdocs/ui/components/text-input.md

Text input

Fields the user types a value into.

← Component reference

TextInput #

Text field. Every other attribute (id, name, autocomplete, aria-*, …) lands on the <input>.

function Example() {
    const name = signal("");
    return (
        <TextInput
            aria-label="Full name"
            placeholder="Ada Lovelace"
            value={name()}
            onInput={(e) => name.set((e.target as HTMLInputElement).value)}
        />
    );
}
PropTypeRequiredDefaultDescription
disabledbooleanDisables the field.
invalidbooleanMarks the value invalid (aria-invalid and error styling).
namestringField name submitted with the form.
onBlur(e: FocusEvent) => voidCalled when the field loses focus.
onFocus(e: FocusEvent) => voidCalled when the field gains focus.
onInput(e: InputEvent) => voidCalled on every edit; read e.target.value.
onKeyDown(e: KeyboardEvent) => voidCalled on key presses.
placeholderstringHint shown while empty.
typestring"text"Native input type (text, email, url, tel, …).
valuestring""Current text (controlled; update it from onInput).

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

TextArea #

Multi-line text field.

function Example() {
    const message = signal("");
    return (
        <TextArea
            aria-label="Message"
            rows={3}
            placeholder="Tell us what happened…"
            value={message()}
            onInput={(e) => message.set((e.target as HTMLInputElement).value)}
        />
    );
}
PropTypeRequiredDefaultDescription
disabledbooleanDisables the field.
invalidbooleanMarks the value invalid (aria-invalid and error styling).
namestringField name submitted with the form.
onBlur(e: FocusEvent) => voidCalled when the field loses focus.
onInput(e: InputEvent) => voidCalled on every edit; read e.target.value.
placeholderstringHint shown while empty.
rowsnumberVisible text lines.
valuestring""Current text (controlled; update it from onInput).

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Number input #

Numbers and quantities with steppers.

NumberInput #

Numeric field. Typing keeps a free-form draft (so 15 can be typed with min=10); values commit when in range and clamp on blur / Enter / step. Results are rounded to the precision of step (no 0.30000000000000004). The <input> is the host (class, style, id, aria-* land on it); classes.root styles the wrapper.

function Example() {
    const seats = signal(5);
    return (
        <NumberInput aria-label="Seats" min={1} max={50} value={seats()} onChange={seats.set} />
    );
}

Slots: root decrement increment input

PropTypeRequiredDefaultDescription
onChange(value: number) => voidyesCalled with the new number (never NaN).
valuenumberyesCurrent value (controlled).
decrementLabelstring"Decrease"Accessible labels for the step buttons.
disabledbooleanDisables the field and its buttons.
incrementLabelstring"Increase"Accessible name of the + button.
invalidbooleanMarks the value invalid (aria-invalid and error styling).
maxnumberHighest value; typing above it is clamped on blur.
minnumber0Lowest value; typing below it is clamped on blur.
namestringField name submitted with the form.
stepnumberAmount the − / + buttons and arrow keys change the value by.

QuantityInput #

− value + stepper.

2
function Example() {
    const quantity = signal(2);
    return <QuantityInput min={1} max={10} value={quantity()} onChange={quantity.set} />;
}

Slots: root button value

PropTypeRequiredDefaultDescription
onChange(value: number) => voidyesCalled with the new quantity.
valuenumberyesCurrent quantity (controlled).
disabledbooleanDisables both buttons.
labelstring"Quantity"Accessible name for the stepper group (default "Quantity").
maxnumber99Highest quantity (the + button disables there).
minnumber1Lowest quantity (the − button disables there).

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Password #

Password entry and strength feedback.

PasswordInput #

Password field with visibility toggle. The <input> is the host (class, id, autocomplete, aria-* land on it); classes.root styles the wrapper.

function Example() {
    const password = signal("");
    return <PasswordInput aria-label="Password" value={password()} onChange={password.set} />;
}

Slots: root input toggle

PropTypeRequiredDefaultDescription
onChange(value: string) => voidyesCalled with the new password on every edit.
valuestringyesCurrent password (controlled).
disabledbooleanDisables the field.
hideLabelstring"Hide password"Accessible name of the reveal button while shown.
invalidbooleanMarks the value invalid (aria-invalid and error styling).
namestringField name submitted with the form.
placeholderstringHint shown while empty.
showLabelstring"Show password"Accessible name of the reveal button while hidden.

PasswordStrength #

Four-bar strength meter.

Good
<PasswordStrength password="correct horse" />

Slots: root bar bars label

PropTypeRequiredDefaultDescription
passwordstringyes""The password to rate (strength bar and hints).
labels[string, string, string, string, string]Override the five labels (score 0–4).

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

SearchInput #

Search field with clear button. The <input> is the host; classes.root styles the wrapper.

function Example() {
    const query = signal("");
    return (
        <SearchInput
            aria-label="Search projects"
            placeholder="Search projects"
            value={query()}
            onChange={query.set}
        />
    );
}

Slots: root clear icon input

PropTypeRequiredDefaultDescription
onChange(value: string) => voidyesCalled with the new query on every edit (and with "" when cleared).
valuestringyesCurrent query (controlled).
clearLabelstring"Clear search"Accessible name of the clear (×) button.
disabledbooleanDisables the field.
iconcontentLeading icon (default: the search icon); null hides it.
onSubmit() => voidCalled when the user presses Enter.
placeholderstring"Search"Hint shown while empty.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

PinInput #

One-time-code input: one cell per digit, paste fills all cells. Forwarded attributes land on the group.

Enter the 6-digit code.

function Example() {
    const code = signal("");
    return (
        <>
            <PinInput
                aria-label="Verification code"
                length={6}
                value={code()}
                onChange={code.set}
            />
            <Text muted>{code().length === 6 ? `Verifying ${code()}…` : "Enter the 6-digit code."}</Text>
        </>
    );
}

Slots: root cell

PropTypeRequiredDefaultDescription
onChange(value: string) => voidyesCalled with the new code on every edit or paste.
valuestringyesDigits entered so far (controlled).
disabledbooleanDisables every cell.
lengthnumber4Number of cells.
maskbooleanMask digits like a password.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

TagsInput #

Free-form tag entry (Enter / comma / paste lists, case-insensitive dedupe).

signalsssr
function Example() {
    const topics = signal(["signals", "ssr"]);
    return (
        <TagsInput
            aria-label="Topics"
            placeholder="Add a topic and press Enter"
            value={topics()}
            onChange={topics.set}
        />
    );
}

Slots: root input remove tag

PropTypeRequiredDefaultDescription
onChange(value: string[]) => voidyesCalled with the new tags (added with Enter or comma; removed with ×).
valuestring[]yesCurrent tags (controlled).
disabledbooleanDisables adding and removing.
placeholderstring"Add tag"Input hint.
renderTag(tag: string) => unknownRender a tag's content (default: the text).

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

InputGroup #

Joins inputs and addons into one control.

https://.arachne.app
<InputGroup>
    <InputAddon>https://</InputAddon>
    <TextInput aria-label="Subdomain" value="acme" />
    <InputAddon>.arachne.app</InputAddon>
</InputGroup>
PropTypeRequiredDefaultDescription
childrencontentInputs, buttons and InputAddons to join edge to edge.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Parts

InputAddon #

Static addon (text, icon) attached to an input.

$
<InputGroup>
    <InputAddon>$</InputAddon>
    <TextInput aria-label="Amount" value="49.00" />
</InputGroup>
PropTypeRequiredDefaultDescription
childrencontentAddon text or content, e.g. https:// or a unit.

InlineEdit #

Click-to-edit text.

function Example() {
    const name = signal("Marketing site");
    return <InlineEdit label="Project name" value={name()} onChange={name.set} />;
}

Slots: root display icon input

PropTypeRequiredDefaultDescription
onChange(value: string) => voidyesCalled with the new text when an edit is committed (Enter or blur); Escape cancels.
valuestringyesCurrent text (controlled).
labelstringAccessible label for the edit button / input.
placeholderstringText shown while the value is empty.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

JsonInput #

JSON text area that flags invalid JSON (aria-invalid, data-invalid).

function Example() {
    const config = signal('{\n  "region": "fra1",\n  "replicas": 2\n}');
    return <JsonInput aria-label="Config JSON" value={config()} onChange={config.set} />;
}
PropTypeRequiredDefaultDescription
onChange(value: string) => voidyesCalled with the new text on every edit (invalid JSON is flagged, not rejected).
valuestringyesJSON text (controlled).
disabledbooleanDisables the field.
namestringField name submitted with the form.
rowsnumber6Visible text lines.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

Secret & copy fields #

Read-only values to reveal or copy.

SecretField #

Masked secret with reveal / copy.

Webhook secret••••••••••••••••
<SecretField label="Webhook secret" value="whsec_9f2kQ83m1x" />

Slots: root actions label value

PropTypeRequiredDefaultDescription
valuestringyesThe secret (masked until revealed).
labelcontentField label.

CopyField #

Read-only value with a copy button.

API keysk_live_51Hx…9fQ2
<CopyField label="API key" value="sk_live_51Hx…9fQ2" />

Slots: root action label value

PropTypeRequiredDefaultDescription
valuestringyesText shown and copied.
labelstringField label (also the copy button's accessible context).

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.