Text Field
A single-line text input with label, help text, validation and optional prefix/suffix, plus the field primitives other inputs compose.
Preview
Installation
pnpm add @syntara/react @syntara/tokensimport { TextField, Label, Description, FieldError, Input, FieldGroup } from '@syntara/react';Usage
import { TextField } from '@syntara/react';
export function EmailField() {
return <TextField label="Email" type="email" description="We'll send receipts here." isRequired />;
}Examples
States
Default, required with description, invalid and disabled.
Prefix and suffix
Currency, units or icons inside the input box.
Validation
Native required checks plus a validate() function, shown after submit.
Sizes
sm, md and lg, each next to a Button of the same size: same height, same corner.
Accessibility
| Keys | Action |
|---|---|
| Tab | Moves focus to the input. |
| Enter | Submits the surrounding form. |
- Label, description and error message are linked to the input with aria-labelledby and aria-describedby.
- Errors show an icon and text, never colour alone.
- The required asterisk is aria-hidden: the input is announced as required through the native required attribute.
- Text inputs show the focus ring whenever focused, as browsers do for text inputs.
- The field edge is a 1px boundary of at least 3:1 against canvas, default, raised and sunken surfaces in every brand and scheme (WCAG 1.4.11): border.strong in light, a mix of 80% border.strong and 20% border.default in dark. The test "field boundary contrast" proves it over the 5 tenants and 1,000 fuzz brands.
Guidelines
Do
- Always give a visible label; put format hints in description.
- Use the right type and inputMode so mobile keyboards match the data.
- Validate on submit and explain how to fix the error.
- In a row with a Button, give both the same size: they share height and corner radius.
Don’t
- Don't use the placeholder as the label.
- Don't show an error before the person has had a chance to type.
API reference
TextField
labelReactNodeVisible label, linked to the control. Without one, pass aria-label.
descriptionReactNodeHelp text, linked with aria-describedby.
errorMessagestring | ((validation: ValidationResult) => string)Shown with an icon when invalid. Defaults to the browser or validate() message.
placeholderstringExample value. Not a substitute for a label.
typetextdefaultemailtelurlpasswordsearch
Native input type.
prefixReactNodeContent before the input, inside the box (text in text.subtle, or an icon).
suffixReactNodeContent after the input, inside the box (a unit, icon or small button).
value / defaultValuestringControlled / uncontrolled value.
onChange(value: string) => voidCalled on every edit.
isRequiredbooleanMarks the field required (native required + a decorative asterisk on the label).
Default
falseisInvalidbooleanForces the invalid state (aria-invalid, danger border, error message).
Default
falseisDisabledbooleanDisables the field.
Default
falsevalidate(value) => string | string[] | true | null | undefinedCustom validation; return an error message to show.
inputRefRef<HTMLInputElement>Ref to the underlying input.
sizesmmddefaultlg
Height of the box: md is the density's control height, sm one 8px step shorter, lg two steps taller. Height, corner radius and inline inset match a Button of the same size, so a field and a button in one row line up.
Label
isRequiredbooleanAdds a decorative (aria-hidden) asterisk; the control carries the required state.
Default
false
Description
childrenReactNodeHelp text. Renders React Aria's Text with slot="description".
FieldError
childrenReactNode | ((validation: ValidationResult) => ReactNode)Error text with an alert icon; renders nothing while the field is valid.
Input
classNamestring | ((renderProps) => string)The bordered input. Inside a FieldGroup it becomes bare and the group draws the box.
FieldGroup
childrenReactNode | ((renderProps) => ReactNode)Input plus adornments. Draws the box, hover/invalid/disabled states and the focus ring; a click on it focuses the input.
Tokens
The semantic tokens this component reads, grouped by what they control. Swatches show this site’s theme; change a tenant’s brand and the component follows with no code change.
- Colour11
border.defaultborder.strongfeedback.danger.fgfeedback.danger.solidfocus.ringsurface.defaultsurface.sunkentext.defaulttext.disabledtext.subtlesurface.canvas- Type9
font.size.mdfont.size.smfont.size.xsfont.weight.mediumline-height.normalline-height.snugfont.tracking.mdfont.tracking.smfont.tracking.xs- Space and size5
control-heightcontrol-padding-inlinespace.1space.2space.4- Shape1
radius.field- Motion6
motion.duration.fastmotion.duration.normalmotion.easingmotion.easingOutmotion.duration.springmotion.spring