Form Structure
Components

Form Structure

Field, FieldRow, FormGroup, Fieldset, FieldControl, and ValidationMessage: the layout and validation layer for Onset forms.

Components in this category

Field

Field wraps an input with label, description hint, and validation error in one vertical row. It publishes an error state to the controls below via an internal context: any Control-based input inside reads that state and applies its own invalid styling without a per-control flag.

FieldProps props
PropTypeDefaultDescription
labelReactNode—The field label.
hintReactNode—Dim helper line under the control.
errorReactNode—Validation error message. Replaces hint when set.
requiredboolean—Shows a '*' next to the label.
labelMetaReactNode—Right-aligned meta next to the label (e.g. remaining chars).
htmlForstring—Associates the label with the input's id.
childrenReactNode—The input control.
Field

The error context cascades: every Input, Textarea, and NumberInput inside an errored Field renders with a red border and focus ring. An explicit invalid prop on the control itself still wins.

ImportTSX
import { Field, Input } from "@solarius/onset";

<Field label="API Key" hint="Found in Settings → API Keys" required>
  <Input placeholder="sk_live_..." />
</Field>;

FieldRow

FieldRow places multiple Fields on one line with even widths: the standard pattern for amount/currency, start/end date, or city/zip pairs.

FieldRow
ImportTSX
import { FieldRow } from "@solarius/onset";

<FieldRow>
  <Field label="Amount">
    <Input prefix="$" />
  </Field>
  <Field label="Currency">
    <Select options={currencyOptions} />
  </Field>
</FieldRow>;

FormGroup

FormGroup groups related form rows under one shared validation + disabled state. Anything inside can read that state via useFormGroup(), so a server-side error on "billing address" can mark all fields in the group at once instead of being threaded through each of them.

FormGroupProps props
PropTypeDefaultDescription
childrenReactNode—Form rows.
disabledboolean—Greys out every descendant.
requiredboolean—Marks every FieldControl inside as required.
toneValidationTone—Validation tone inherited by descendants: error, warning, or success.
messageReactNode—Validation summary rendered under the group.
layout"stack" | "row""stack"Stack or side-by-side layout.
gap"tight" | "normal" | "loose""normal"Row spacing.
Import & usageTSX
import { FormGroup, useFormGroup, Field, Input } from "@solarius/onset";

<FormGroup
  disabled={loading}
  tone={serverError ? "error" : undefined}
  message={serverError ? "Billing address is invalid" : undefined}
>
  <Field label="Street">
    <Input placeholder="123 Main St" />
  </Field>
  <Field label="City">
    <Input placeholder="San Francisco" />
  </Field>
  <Field label="ZIP">
    <Input placeholder="94105" />
  </Field>
</FormGroup>;

The useFormGroup() hook returns { tone, message, disabled, required, idPrefix } or null outside a group. Controls can opt into group state without requiring a specific wrapper.

FormGroup

Fieldset

Semantic <fieldset> with a styled legend, sharing FormGroup's validation context. Use this (not FormGroup) when the grouping is meaningful to assistive tech: radio sets, address blocks, permission matrices.

FieldsetProps props
PropTypeDefaultDescription
legendReactNode—The fieldset's legend text.
descriptionReactNode—Dim explanatory line under the legend.
actionReactNode—Right-aligned control in the legend row (e.g. a Switch).
childrenReactNode—Form content.
borderedbooleantrueDraws the bordered box.
toneValidationTone—Validation tone.
messageReactNode—Validation message.
Fieldset
ImportTSX
import { Fieldset } from "@solarius/onset";

<Fieldset
  legend="Notifications"
  description="Manage email preferences"
  action={<Switch />}
>
  <Field label="Email">
    <Input type="email" />
  </Field>
</Fieldset>;

FieldControl

FieldControl is a form row that inherits the surrounding FormGroup's disabled and validation state, and hands its control a generated id via render prop so label association does not have to be wired by hand.

FieldControlProps props
PropTypeDefaultDescription
labelReactNode—The row label.
childrenReactNode | ((props: { id, disabled, invalid }) => ReactNode)—The control, or a render prop receiving derived props.
hintReactNode—Helper text under the control.
toneValidationTone—Overrides the group's tone for this row only.
messageReactNode—Validation message for this row.
requiredboolean—Overrides the group's required flag.
disabledboolean—Overrides the group's disabled flag.
labelMetaReactNode—Right-aligned meta next to the label.
ImportTSX
import { FieldControl } from "@solarius/onset";

<FieldControl label="Team name" required>
  {({ id, invalid }) => (
    <Input id={id} invalid={invalid} placeholder="My team" />
  )}
</FieldControl>;

ValidationMessage

Single-line validation note with a matching icon: error, warning, or success. Used internally by FormGroup and Fieldset, but exported standalone so a custom form layout can display its own summaries without adopting the full group model.

ValidationMessageProps props
PropTypeDefaultDescription
toneValidationTone"error"Validation tone controlling icon and color: error, warning, or success.
childrenReactNode—The message text.
ValidationMessage
ImportTSX
import { ValidationMessage } from "@solarius/onset";
import type { ValidationTone } from "@solarius/onset";

Putting it together

A realistic form section: Fieldset with a toggle action, validation tone from a server error, and a FieldRow for the city/state pair:

Complete form section