Skip to content

TextField

Text field based on react-aria-components: compose Label, Input (or TextArea), an optional Description and FieldError inside it. RAC wires the ids and routes the matching validationErrors entry to FieldError.

import { TextField } from "@evolve-storefront/ui/text-field";
Design notes

The props are declared here rather than derived from RAC's, so the primitive's dialect (isDisabled, isInvalid, onChange(value)) never becomes our contract — swapping the primitive is then this file's problem, not every consumer's.

Preview

DefaultOpen in Storybook ↗

Usage

<TextField name="email" type="email" autoComplete="email" required>
<Label>{t("email")}</Label>
<Input placeholder={t("emailPlaceholder")} />
<FieldError />
</TextField>;

Props

PropTypeDescription
name?string
type?"search" | "text" | "email" | "date" | "password" | "tel" | "url"

date is here because a date range is two text fields, not a widget: the native control submits a yyyy-mm-dd string in a plain GET form, which is what a filter that has to survive a shared URL and a cold load needs.

value?string
defaultValue?string
onValueChange?((value: string) => void)

onValueChange, not onChange: this hands over the value, where the DOM event of that name hands over an event. Same-name-different-signature is the one shadow nothing catches at the call site.

disabled?boolean
required?boolean
readOnly?boolean
invalid?boolean
autoComplete?string
children?ReactNode

Any other props go to the root element, like on a plain HTML element.

Change the layout

Pass children to TextField to arrange its parts yourself. Every part is exported from @evolve-storefront/ui/text-field.

<TextField name="email" type="email" autoComplete="email" required>
<Label>{t("email")}</Label>
<Input placeholder={t("emailPlaceholder")} />
<FieldError />
</TextField>;
DescriptionHelp text under the control.

No props of its own.

FieldErrorThe field's validation message.
PropTypeDescription
children?ReactNode

Omit to render whatever RAC's validation put on the field.

Any other props go to the root element, like on a plain HTML element.

InputThe single-line control, inside TextField.
PropTypeDescription
data-component?undefined

Not a caller's to rename — see controlProps.

Any other props go to the root element, like on a plain HTML element.

TextAreaThe multi-line control, inside TextField.
PropTypeDescription
data-component?undefined

Not a caller's to rename — see controlProps.

Any other props go to the root element, like on a plain HTML element.

Examples

With Description
<TextField name="email" type="email">
<Label>{t("email")}</Label>
<Description>{t("emailDescription")}</Description>
<Input />
<FieldError />
</TextField>;
Open in Storybook ↗
With ErrorErrors flow from the Form's validationErrors (e.g.
// The Form routes each error to the field with that name.
<Form validationErrors={result.error?.flatten().fieldErrors}>
<TextField name="email" type="email">
<Label>{t("email")}</Label>
<Input />
<FieldError />
</TextField>
</Form>;
Open in Storybook ↗
Multiline
<TextField name="notes">
<Label>{t("notes")}</Label>
<TextArea placeholder={t("notesPlaceholder")} />
<FieldError />
</TextField>;
Open in Storybook ↗