Forms

A guide to building forms with Cubby UI components

Overview

Cubby UI provides three components for building accessible forms: Form, Field, and Fieldset. These wrap Base UI's form primitives, providing native constraint validation, custom validation, accessible labeling, error display, and integration with third-party libraries.

Quick start

Labeling form controls

Every form control needs an accessible label. The correct label component depends on the control type.

Input-type controls

Use FieldLabel to label these controls:

For Checkbox, Radio, and Switch, wrap the control inside FieldLabel for implicit labeling:

Trigger-based controls

These controls have their own label component. Do not use FieldLabel for them:

Descriptions

FieldDescription adds accessible helper text to any field, regardless of control type:

Grouping controls with Fieldset

Use Fieldset with FieldsetLegend when a single label applies to multiple controls, such as radio groups, checkbox groups, or multi-thumb sliders. Compose with the render prop to merge the fieldset element with a group component:

Use FieldItem to wrap each individual option so it gets its own label and description.

Validation

Constraint validation

Use native HTML attributes on FieldControl (required, pattern, minLength, type, min, max, etc.) and FieldError to display the browser's native error message:

Per-state custom messages

To override native messages (for i18n or branding), use multiple FieldError components with the match prop — each one renders only for the specified validity state:

Available match values: valueMissing, typeMismatch, patternMismatch, tooShort, tooLong, rangeUnderflow, rangeOverflow, stepMismatch, badInput, customError.

Custom validation

Use the validate prop on Field for custom logic. Supports async functions:

Server-side validation

Pass errors from your server to Form's errors prop. Keys match field name attributes:

Submitting data

Native onSubmit

Use onSubmit with FormData for standard form submission:

JavaScript object with onFormSubmit

Use onFormSubmit to receive values as a plain object. preventDefault is called automatically:

Third-party library integration

React Hook Form

Use Controller to bridge RHF state with Field's invalid, dirty, and touched props. Use onValueChange on FieldControl (not onChange) and match={!!error} on FieldError:

For RHF to focus invalid fields, forward ref to the underlying control - typically via inputRef on wrapper components (Select, Switch, Checkbox, RadioGroup) or directly as ref on input-like components.

TanStack Form

Use form.Field (TanStack's field) to manage state, and Field (Cubby UI) for the UI. Note: the Base UI Form component isn't needed with TanStack Form - use a native <form> element instead.

Component reference

ComponentUse for
FormForm wrapper with errors, onFormSubmit, validationMode
FieldField wrapper with label, control, description, error, validation
FieldsetGrouping related fields with a shared legend