Components

FormField

Composite form field providing accessible label, control, description, and error message wiring via context.

Category: Form / Composite

Import

import {
  FormField,
  FormFieldLabel,
  FormFieldControl,
  FormFieldDescription,
  FormFieldMessage,
} from "@hareru/ui"

Exports

NameTypeDescription
FormFieldComponentRoot container; generates a unique id and provides context
FormFieldLabelComponent<label> with htmlFor injected from context
FormFieldControlComponentWrapper that injects id, aria-describedby, and aria-invalid into its single child
FormFieldDescriptionComponentHelper text with stable id for aria-describedby
FormFieldMessageComponentError or validation message; renders as role="alert" when in error state

Types

export interface FormFieldProps extends React.HTMLAttributes<HTMLDivElement> {
  error?: boolean
}

export interface FormFieldLabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {}
export interface FormFieldControlProps extends React.HTMLAttributes<HTMLDivElement> {}
export interface FormFieldDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {}
export interface FormFieldMessageProps extends React.HTMLAttributes<HTMLParagraphElement> {}

Key Props

PropTypeComponentDefaultDescription
errorbooleanFormFieldfalseActivates error styles and role="alert" on the message

Accessibility Wiring

FormFieldControl automatically injects the following props into its direct child element:

Injected PropValuePurpose
idAuto-generated UUIDAssociates control with label
aria-describedby"<id>-description <id>-message"Links control to description and message
aria-invalidtrue (only when error=true)Signals invalid state to screen readers

Structure

  • FormFieldLabel [label]
  • FormFieldControl [control] (expected)
  • FormFieldDescription [description]
  • FormFieldMessage [description]

(expected) = recommended in canonical composition, not runtime-required.

Accessibility

  • Notes: Automatically links label, input, and error message via aria-describedby.

Usage

import {
  FormField,
  FormFieldLabel,
  FormFieldControl,
  FormFieldDescription,
  FormFieldMessage,
  Input,
  Button,
} from "@hareru/ui"

// Basic form field
<FormField>
  <FormFieldLabel>Email</FormFieldLabel>
  <FormFieldControl>
    <Input type="email" placeholder="you@example.com" />
  </FormFieldControl>
  <FormFieldDescription>The email address used for login</FormFieldDescription>
</FormField>

// With validation error
<FormField error={!!errors.name}>
  <FormFieldLabel>Name</FormFieldLabel>
  <FormFieldControl>
    <Input placeholder="John Doe" />
  </FormFieldControl>
  <FormFieldDescription>Used as your display name</FormFieldDescription>
  <FormFieldMessage>Name is required</FormFieldMessage>
</FormField>

// With Textarea
<FormField error={!!errors.bio}>
  <FormFieldLabel>Bio</FormFieldLabel>
  <FormFieldControl>
    <Textarea rows={4} placeholder="Tell us about yourself..." />
  </FormFieldControl>
  <FormFieldMessage>{errors.bio?.message}</FormFieldMessage>
</FormField>

Notes

  • FormFieldControl expects exactly one child element. Wrapping multiple elements will throw.
  • FormFieldMessage renders null when children is empty, so it is safe to always include it.
  • FormFieldLabel and FormFieldControl must be used within <FormField> — they will throw if the context is missing.
  • The error prop on FormField propagates to both FormFieldLabel (error style) and FormFieldMessage (role="alert").

On this page