Skip to content

Text Field

Single-line or multi-line text input with a label, help text and error message.

Inputv0.1.1experimentalWCAG 2.2 AAView spec
import { TextField } from "@rdloom/react";

export default function TextFieldBasicExample() {
  return (
    <TextField className="w-64" label="Full name" />
  );
}

Installation

npx rdloom add text-field

Copies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.

Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/text-field.json

Works, but without upgrade tracking.

Usage

import { TextField } from "@rdloom/react";

<TextField className="w-64" label="Full name" />

With description

We only use it for sign-in.
import { TextField } from "@rdloom/react";

export default function TextFieldWithDescriptionExample() {
  return (
    <TextField className="w-64" label="Email" type="email" placeholder="you@company.com" description="We only use it for sign-in." />
  );
}

Invalid

import { Button, TextField } from "@rdloom/react";

export default function TextFieldInvalidExample() {
  return (
    <form className="flex flex-col items-start gap-3" onSubmit={(e) => e.preventDefault()}>
      {/* Declare the rules; errors show on submit and are announced. */}
      <TextField
        className="w-64"
        label="Username"
        isRequired
        validate={(v) => (v.length > 0 && v.length < 3 ? "Use at least 3 characters." : null)}
      />
      <Button type="submit">Create account</Button>
    </form>
  );
}

Multiline

import { TextField } from "@rdloom/react";

export default function TextFieldMultilineExample() {
  return (
    <TextField className="w-80" label="Feedback" multiline placeholder="What could be better?" />
  );
}

Sizes

import { TextField } from "@rdloom/react";

export default function TextFieldSizesExample() {
  return (
    <div className="flex flex-wrap items-end gap-3">
      <TextField className="w-40" label="Small" size="sm" />
      <TextField className="w-40" label="Medium" size="md" />
      <TextField className="w-40" label="Large" size="lg" />
    </div>
  );
}

API Reference

Defined by the spec. Components also accept the props of the React Aria component they wrap.

PropTypeDefault
labelrequired

Visible label. Required so the field is always named.

stringnone
description

Help text shown under the input.

stringnone
errorMessage

Shown when the field is invalid.

stringnone
placeholder

Example value. Never a replacement for the label.

stringnone
size

Height and font size.

"sm" | "md" | "lg""md"
multiline

Renders a textarea instead of an input.

booleanfalse
isDisabled

Prevents editing and dims the field.

booleanfalse
isInvalid

Marks the value as invalid and shows errorMessage.

booleanfalse
isRequired

Marks the field as required for form submission.

booleanfalse
value

Controlled value.

stringnone
defaultValue

Initial value when uncontrolled.

stringnone
onChange

Called with the new value on every edit.

(value: string) => voidnone

Accessibility

Role textbox, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.

Keyboard

  • Tab moves focus in and out

Screen readers announce

  • On focus: the label, then the field type (edit text), then the description
  • Required fields are announced as required
  • Invalid: announced as invalid, and the error message is read
  • Multiline fields are announced as multi-line

What your code must do

  • Label is always rendered and linked to the input
  • Description and error are linked with aria-describedby
  • Invalid state is exposed with aria-invalid

Guidelines

Use it when

  • Collecting free-form text such as names, emails or notes

Avoid it when

  • Picking from a known list: use Select or Combobox
  • Dates: use DatePicker

Don't

  • Placeholder used as the only label
  • Showing errors before the user has finished typing
  • Passing isInvalid for required or format checks (even isInvalid={false}): any defined value takes over validity and hides the built-in errors. Use isRequired and validate; pass isInvalid only for errors the app finds itself, such as from the server

Design tokens

The semantic tokens this component uses. Change them once and every component follows; see Design tokens.

  • --rd-color-surface-default
  • --rd-color-border-default
  • --rd-color-border-strong
  • --rd-color-text-default
  • --rd-color-text-muted
  • --rd-color-feedback-danger
  • --rd-color-focus-ring
  • --rd-radius-control