- Accordion
- Action Bar
- Action Toast
- Alert Banner
- Alert Dialog
- Alert
- Alphabet Index
- Amount Field
- Aspect Ratio
- Attachment
- Availability List
- Avatar
- Badge
- Bank Card
- Breadcrumb
- Bubble
- Button Group
- Button
- Calendar
- Camera Frame
- Card Design Picker
- Card Details
- Card Peek
- Card Stack
- Card
- Carousel
- Chart
- Chat Thread
- Checkbox
- Chip Group
- Choice Tile
- Code Block
- Code Dots
- Collapsible
- Combobox
- Command
- Context Menu
- Copy Field
- Countdown
- Country Select
- Data Table
- Date Field
- Date Picker
- Date Range Picker
- Dialog
- Direction
- Document Paper
- Drawer
- Dropdown Menu
- Empty
- Entity Avatar
- Field
- File Trigger
- File Upload Item
- Hover Card
- Input Group
- Input OTP
- Input
- Item
- Kbd
- Key Value
- Keypad
- Label
- Live Region
- Marker
- Message Scroller
- Message
- Money Amount
- Native Select
- Number Field
- Pagination
- Percent Split Editor
- Phone Field
- Popover
- Progress
- QR Code
- Questionnaire
- Radio Group
- Range Histogram
- Rating
- Resizable
- Scroll Area
- Search Field
- Select
- Separator
- Share Bar
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Sortable List
- Spinner
- Status Timeline
- Step Header
- Steps List
- Swipe Row
- Switch
- Table
- Tabs
- Text Field
- Textarea
- Tier Ladder
- Toggle Group
- Toggle
- Token Search Field
- Tooltip
- Trace
- Transaction Row
- Usage Meter
"use client"
import * as React from "react"The TextField component is a labelled single-line input built on the React Aria TextField. The label, the description, and the error are linked to the input for you, with no id, htmlFor, aria-invalid, or aria-describedby by hand. Use it for names, handles, emails, references, and promo codes. For money amounts, phone numbers, or one-time codes, use Amount Field, Phone Field, or Input OTP; for a layout the props cannot express, compose Field with Input yourself.
Installation#
pnpm dlx shadcn@latest add @dots/text-field
Usage#
import { TextField } from "@/components/ui/text-field"<TextField
label="Reference"
description="The recipient sees this."
value={reference}
onChange={setReference}
/>Text Field vs Field#
Use TextField for a single-line text input with a label, a description, and an error. It renders the whole field from props and keeps the error and description linked to the input.
Use Field with Input or InputGroup when you need a custom layout, such as a horizontal field, several inputs under one label, or a control other than a text input.
Features#
- Label, description, and error linked to the input automatically
- The error replaces the description while the field is invalid
- The error is read through
aria-describedbywhen the field gets focus, never announced as an alert prefixandsuffixinside the field, such as@or a spinner, without remounting the input when they come and go- Native or custom validation through React Aria, including
isRequiredandvalidate focusOrAnnounceto bring the user to the field after a failed submit
Description#
Add a description for help that the user needs before typing. It is hidden while an error is shown.
Only you see this note.
"use client"
import { TextField } from "@/components/ui/text-field"Prefix#
Pass prefix to show text or an icon before the value, such as @ for a username. See the demo at the top of this page.
<TextField
label="Username"
prefix="@"
autoCapitalize="none"
autoCorrect="off"
spellCheck="false"
/>Suffix#
Pass suffix to show text, an icon, or an InputGroupButton after the value, such as a spinner while a lookup runs. Passing the prop at all, even as undefined, keeps the input inside an input group, so focus stays in the field while the suffix changes.
Checking…
"use client"
import * as React from "react"Invalid#
Set isInvalid and pass an errorMessage that says what to fix. The error takes the place of the description.
"use client"
import { TextField } from "@/components/ui/text-field"Required#
Set isRequired to check the field on submit with native validation. Without errorMessage, the browser's validation message is shown.
"use client"
import { Button } from "@/components/ui/button"Focus after a failed submit#
When a server check fails after submit, call focusOrAnnounce once the error is rendered. It moves focus to the field so the error is read through aria-describedby. When the field already has focus, because the form was sent with Enter, it announces the error politely instead.
"use client"
import * as React from "react"React.useEffect(() => {
if (attempt && error) focusOrAnnounce(inputRef.current, error)
}, [attempt, error])Disabled#
Set isDisabled for a value the user cannot change here. Use the description to say how to change it.
Matches your ID. To change it, contact support.
"use client"
import { TextField } from "@/components/ui/text-field"Accessibility#
TextField is built on the React Aria TextField. The label is linked to the input, the invalid and required states are exposed from isInvalid and isRequired, and the description or error is linked through aria-describedby. The error has no role="alert", so it is read when the user reaches the field and not in the middle of typing.
Label the field#
Pass label for a visible label. When the design has no visible label, pass aria-label instead. A placeholder is not a label.
Error text#
Keep the error to what the user should do, such as "Enter the full address". The error icon is decorative, so the meaning is in the text and not only in the color.
Input hints#
Set autoCapitalize="none", autoCorrect="off", and spellCheck="false" for handles, codes, and emails, and set type, inputMode, and autoComplete to match the value, so mobile keyboards and autofill help the user.
API Reference#
TextField#
Also accepts all React Aria TextField props, such as value, defaultValue, onChange, type, name, inputMode, autoComplete, isRequired, isReadOnly, validate, and validationBehavior.
| Prop | Type | Default | Description |
|---|---|---|---|
label | React.ReactNode | - | The visible label above the input. Without it, pass aria-label. |
description | React.ReactNode | - | Help under the input. Hidden while an error is shown. |
errorMessage | React.ReactNode | ((validation: ValidationResult) => React.ReactNode) | - | What to fix. Shown while the field is invalid. Without it, the browser's validation message is shown. |
prefix | React.ReactNode | - | Text or an icon before the value. Passing the prop renders the input inside an input group. |
suffix | React.ReactNode | - | Text, an icon, or an InputGroupButton after the value. Passing the prop renders an input group. |
placeholder | string | - | A sample value shown while the input is empty. |
autoCapitalize | React.InputHTMLAttributes<HTMLInputElement>["autoCapitalize"] | - | Use "none" for handles, codes, and emails. |
inputRef | React.Ref<HTMLInputElement> | - | A ref to the input element, such as for focusOrAnnounce. |
isInvalid | boolean | - | Whether the value is invalid. |
isDisabled | boolean | - | Whether the field is disabled. |
className | string | - | Additional classes to apply to the root element. |
focusOrAnnounce#
Call it after a failed submit, once the error is rendered.
function focusOrAnnounce(input: HTMLInputElement | null, message: string): void| Parameter | Type | Description |
|---|---|---|
input | HTMLInputElement | null | The input to focus. Nothing happens when it is null. |
message | string | The error to announce when the input already has focus. |