TextField
A labelled box for a line of text, with a hint, an error and optional icons.
import { TextField } from "@butteryui/react";Demo
Shown on your receipts.
When to use it
Use it for
- A name, an email address, a search, a title: any single line of text
- Sign-in and sign-up forms, at size lg
- A field on a card or in a toolbar, with variant="soft"; a dense panel or a toolbar, with size="sm"
Not for
- A password: use PasswordField
- Text that can run to several lines: use TextArea
- An exact number with steps: use NumberField
Examples
Email field
An email address checked when you leave the field, with the browser's autofill.
Gift card title
A gift card's title with a hint, uncontrolled: the form reads it when it's sent.
Shipping address
A shipping address form: a field for each line, two side by side where they're short, checked on Save.
Shop name
A shop's name kept in state, which also writes the web address the shop will get.
Name your shop to get its address
Sizes
The sizes and looks: a small soft field for a toolbar, md for forms in an app, lg with 16px text for a sign-in page.
Variants and states
States
Empty, filled with a hint, with an error, and disabled.
Shown large on the gift card
Enter an email address like name@example.com.
Sizes and icons
28px for a toolbar or a dense panel; 34px for app forms; 48px with 16px text for sign-in pages.
Outline and soft
Outline, the default, has a 1px edge; soft sits on bg-control and shows its edge only on hover and while it has focus. An error's edge shows in both.
Write at least three notes.
Props
TextField
| Prop | Type | Default | Description |
|---|---|---|---|
labelrequired | string | What the field asks for, shown above it. | |
hint | ReactNode | A hint under the label, such as the format expected. | |
error | ReactNode | What's wrong, shown under the field and read with it. Leave it out while nothing is. | |
value | string | The text. Pair with onValueChange or onChange to control it. | |
defaultValue | string | The starting text when uncontrolled. | |
onValueChange | ((value: string) => void) | Called with the text on every change. | |
size | TextFieldSize | "md" | "md" (34px, 30 compact, the default), "sm" (28px) for a toolbar or a dense panel, or "lg" (48px, 40
compact, with 16px text) for sign-in pages. |
variant | "outline" | "soft" | "outline" | "outline" (the default), a 1px control-border edge on bg-surface, or "soft", a bg-control fill whose
edge shows only on hover and while it has focus, for a field on a card or in a toolbar. |
iconStart | ComponentType<IconComponentProps> | An icon at the start of the box, such as Mail from lucide-react. Decorative. | |
iconEnd | ComponentType<IconComponentProps> | An icon at the end of the box. Decorative. | |
announceError | boolean | true | Whether a new error is read out as it appears. Defaults to true; turn it off when an error summary
that takes focus reads the errors instead. |
It also takes Omit<ComponentPropsWithRef<"input">, "size" | "value" | "defaultValue" | "children">.
TextField.Shell
| Prop | Type | Default | Description |
|---|---|---|---|
rootrequired | { readonly className: string | undefined; readonly "data-bt": string; } | The root's class and data-bt, so each field keeps its own name. | |
end | ReactNode | Something after the input inside the box, such as a reveal button. | |
after | ReactNode | Content under the error, such as a password's requirements. | |
afterId | string | The id of after, read after the hint and the error when the input is described. | |
labelrequired | string | What the field asks for, shown above it. | |
hint | ReactNode | A hint under the label, such as the format expected. | |
error | ReactNode | What's wrong, shown under the field and read with it. Leave it out while nothing is. | |
value | string | The text. Pair with onValueChange or onChange to control it. | |
defaultValue | string | The starting text when uncontrolled. | |
onValueChange | ((value: string) => void) | Called with the text on every change. | |
size | TextFieldSize | "md" | "md" (34px, 30 compact, the default), "sm" (28px) for a toolbar or a dense panel, or "lg" (48px, 40
compact, with 16px text) for sign-in pages. |
variant | "outline" | "soft" | "outline" | "outline" (the default), a 1px control-border edge on bg-surface, or "soft", a bg-control fill whose
edge shows only on hover and while it has focus, for a field on a card or in a toolbar. |
iconStart | ComponentType<IconComponentProps> | An icon at the start of the box, such as Mail from lucide-react. Decorative. | |
iconEnd | ComponentType<IconComponentProps> | An icon at the end of the box. Decorative. | |
announceError | boolean | true | Whether a new error is read out as it appears. Defaults to true; turn it off when an error summary
that takes focus reads the errors instead. |
It also takes Omit<ComponentPropsWithRef<"input">, "size" | "value" | "defaultValue" | "children">.
Accessibility
The visible label names the input and the hint and error describe it, with aria-invalid while there's an error; a new error is read out with the field's name as it appears (announceError={false} where an error summary reads it). The box has a 3:1 edge. Pass type, autoComplete and inputMode so browsers and password managers can fill it in and phones show the right keyboard. The whole box takes a press, and the focus ring shows on it.
| Keys | What they do |
|---|---|
| Letters, numbers and symbols | Types them in |
| Enter | Submits the form it's in, as any text input does |
Motion
It doesn't move.
With reduced motion: Nothing changes.
Layout
It fills its container's width. The box is --bt-h-control (34px, 30 compact) tall, --bt-h-sm (28px) at size="sm", or --bt-h-cta (48px, 40 compact) with 16px text at size="lg", and the whole box is the hit area, its icons included; an outline box draws a 1px edge, and a soft one keeps that edge transparent until hover or focus, so the box never moves; text longer than the box scrolls inside the input. The label, the hint and the error sit above and below the box and wrap.
Related
See it in the roastery demo's settings. See it in the Halfmoon shop demo. See it in the Deckhand demo.