Skip to main content
BM

TextField

A labelled text input or textarea, with an optional hint and error. A single-line field can also have a start icon and clear button.

View source on GitHub

Only used to reply to you.

TextFieldExample.tsx
import { TextField } from "@/components/TextField";

export function TextFieldExample() {
  return (
    <TextField
      label="Email"
      type="email"
      hint="Only used to reply to you."
      autoComplete="email"
      required
    />
  );
}

Examples

Hint

Letters and numbers only.

Error

Enter an email address, like name@example.com.

Required

Optional

Multiline

A few lines is plenty.

Sizes

Clearable

Start icon

Hidden label

Disabled

Props

Unlike the other components, TextField's native props (value, defaultValue, onChange, name, required, disabled, placeholder, autoComplete, ref and the rest) go to the <input>, or the <textarea> in a multiline field, since they belong to it. className and style go to the wrapper, so layout and token overrides cover the whole field. Clearing sets the value and fires an ordinary change, so onChange sees it as it sees typing.

TextField props
PropTypeDefaultDescription
labelstring—The visible label and the field's accessible name. Required by the type. Plain text, so the clear button's name can be built from it.
hideLabelbooleanfalseHides the label visually; it still names the field. Only where something else on screen says what the field is for. It can't be combined with required or optional, since their visible cues are in the label.
optionalbooleanfalseAdds a visible "(optional)" to the label, for a form that marks its optional fields. It can't be combined with required or hideLabel.
hintReactNode—Help text under the label, read as the field's description.
errorReactNode—An error message under the field, with a warning icon. Marks the field invalid and is read after the hint.
type"text" | "search" | "email" | "url" | "tel" | "number""text"The native input type. "search" adds a search icon that hides while the field has focus, and the clear button. "number" takes the native min, max and step.
size"sm" | "md""md"32px or 40px tall, matching Button. In a multiline field, this sets where the first line of text sits.
multilinebooleanfalseRenders a <textarea> that resizes vertically. Native textarea props apply, and type, iconStart, clearable and clearLabel don't.
rowsnumber6A multiline field's visible lines. Only with multiline.
iconStartIconName—A decorative icon at the start of the field. A search field shows "search" unless this names another.
clearablebooleantype === "search"Shows an icon-only clear button while the field has a value, and lets Escape clear it.
clearLabelstring`Clear ${label}`The clear button's accessible name and tooltip.

Component tokens

TextField component tokens
TokenCopyDescriptionAliasesLightDarkPreview
--component-text-field-backgroundThe input's background.color.surface.raised#fffffffrom color.gray.0#18202efrom color.slate.900
--component-text-field-textThe typed value.color.text.default#17202bfrom color.gray.900#fffffffrom color.gray.0
--component-text-field-placeholderPlaceholder text. Never the label.color.text.subtle#5d6876from color.gray.600#d3dce6from color.slate.200
--component-text-field-borderThe input's boundary (3:1 against the surfaces it sits on and its own background).color.border.strong#6d7e90from color.gray.500#818b9efrom color.slate.400
--component-text-field-border-invalidThe boundary while the field has an error.color.error.border#c8372cfrom color.red.600#e8776cfrom color.red.400
--component-text-field-border-widthWidth of the input's boundary.effect.border.width2pxfrom effect.line.22pxfrom effect.line.2
--component-text-field-radiusCorner radius of the input, as on buttons.radius.control4pxfrom radius.44pxfrom radius.4
--component-text-field-height-smInput height at size sm, matching Button.size.control.sm2remfrom size.82remfrom size.8
--component-text-field-height-mdInput height at size md, matching Button.size.control.md2.5remfrom size.102.5remfrom size.10
--component-text-field-padding-inline-smInline padding of the input at size sm.space.inline.sm0.5remfrom space.20.5remfrom space.2
--component-text-field-padding-inline-mdInline padding of the input at size md.space.inline.md1remfrom space.41remfrom space.4
--component-text-field-font-size-smText size of the input at size sm.type.size.small0.875remfrom type.font-size.sm0.875remfrom type.font-size.sm
--component-text-field-font-size-mdText size of the input at size md.type.size.body1remfrom type.font-size.md1remfrom type.font-size.md
--component-text-field-gapSpace between the label, hint, input and error.space.stack.xs0.25remfrom space.10.25remfrom space.1
--component-text-field-label-weightWeight of the label.type.weight.strong600from type.font-weight.600600from type.font-weight.600
--component-text-field-hint-textHint text under the label.color.text.muted#455464from color.gray.700#e6ecf3from color.slate.100
--component-text-field-error-textError message under the input, beside its warning icon.color.error.text#b3261efrom color.red.700#f4a097from color.red.300
--component-text-field-iconThe start icon (decorative; the label names the field).color.text.muted#455464from color.gray.700#e6ecf3from color.slate.100
--component-text-field-icon-sizeSize of the start icon.size.icon.sm1remfrom size.41remfrom size.4
--component-text-field-disabled-backgroundThe input's background while disabled.color.surface.sunken#f2ede2from color.sand.100#060a13from color.slate.1000
--component-text-field-disabled-textThe value's color while disabled.color.text.subtle#5d6876from color.gray.600#d3dce6from color.slate.200

Accessibility

  • A native <input> with a visible <label> tied to it, so clicking the label focuses the field and the label is its name. A label is required by the type.
  • The hint and the error are read as the field's description, hint first. An error also sets aria-invalid, and shows a warning icon beside its text, so it isn't shown by color alone.
  • Required fields use native required, which screen readers announce. The visible "(required)" is hidden from them, so it isn't read twice. It's smaller and muted, so it doesn't read as part of the label.
  • Optional fields say so in the label. Nothing else announces "(optional)", so unlike "(required)" it stays in the accessible name.
  • A multiline field is a native <textarea> with the same label, hint and error behavior, and Enter adds a line. It has no clear button, so a stray Escape can't wipe a long message, and it can be resized vertically.
  • Disabled fields use native disabled: they can't be focused and aren't submitted with a form.
  • The start icon is decorative. A search field's icon is hidden while the field has focus, and the text moves into its space, so there's no gap before what's typed.
  • The clear button is icon-only, named "Clear" and the label, with its name as a tooltip. It follows the field in Tab order and puts focus back in the field. The browser's own search clear button is hidden, so there's only one.
  • The boundary is checked to 3:1 against the surfaces, text and the placeholder to 4.5:1, in both themes. In forced colors the boundary takes a system color and the focus ring stays.
TextField keyboard
KeyAction
TabMoves into the field, then to its clear button when it shows one.
EscapeClears a clearable field that has a value, keeping focus in it. With no value, it does nothing, so a surrounding dialog can still close.
EnterIn a multiline field, adds a line. In a single-line field inside a form, submits the form.