Skip to main content

Field

Associate a control with its label, help, error, and inherited state.

Field is a standalone presentational component with no form validation or form integration. It is used only for rendering field UI, registering controls, inheriting state, and establishing ARIA relationships.

For form integration, use form.Field returned by createForm().

Basic usage#

import { Field, Input } from 'moraine'

export function Example() {
  return (
    <Field label="Email">
      <Input name="email" type="email" />
    </Field>
  )
}

Playground#

Enter an invalid email and leave the input to show the error and enable its slot indicator.

Required

Use your work email address.

We use this address for account notifications.
Props
Slots

Anatomy#

Field [component; slot=root]
├── wrapper [slot]
│   ├── labelWrapper [slot]
│   │   ├── label [slot]
│   │   └── hint [slot]
│   └── description [slot]
└── container [slot]
    ├── help [slot]
    └── error [slot]

Help and error are mutually exclusive; the error replaces help when the field is invalid.

Usage#

Put a Moraine control inside Field so its label, description, and error IDs are registered with the actual focusable element. Standalone Field does not validate or submit values; use form.Field from createForm() when a schema owns the field. For local validation, pass the current message to error.

import { Field, Input } from 'moraine'

export function FieldUsage() {
  return (
    <Field label="Email" description="We'll only use this for account notifications.">
      <Input name="email" placeholder="[email protected]" />
    </Field>
  )
}

Visually hidden labels#

Use hiddenLabel when a compact control needs an accessible name without a visible label. It preserves the label association without reserving label spacing or an empty horizontal label column. A non-empty label takes precedence over hiddenLabel. Hint and description text remain visible; help and error text keep their usual placement.

import { Field, Input } from 'moraine'

export function HiddenLabel() {
  return (
    <div class="flex flex-col gap-4">
      <Field hiddenLabel="Filter commands" help="Search by command name or keyboard shortcut.">
        <Input placeholder="Filter commands…" />
      </Field>
      <Field
        hiddenLabel="Command prefix"
        orientation="horizontal"
        error="Enter a prefix that starts with a letter and contains no spaces."
      >
        <Input placeholder="Command prefix…" />
      </Field>
    </div>
  )
}

For a switch with a trailing visible label, use <Switch label="Auto refresh" />.

Examples#

Inherited state#

import { Field, Switch } from 'moraine'

export function InheritedState() {
  return (
    <Field label="Notifications" size="sm" required>
      <Switch label="Email alerts" />
    </Field>
  )
}

Manual validation#

import { Field, Input } from 'moraine'
import { createSignal } from 'solid-js'

export function ManualValidation() {
  const [error, setError] = createSignal<string | false>()
  return (
    <Field label="Username" error={error()}>
      <Input onValueChange={(value) => setError(value ? false : 'Username is required')} />
    </Field>
  )
}

Horizontal layout#

import { Field, Input } from 'moraine'

export function HorizontalLayout() {
  return (
    <Field orientation="horizontal" label="Display name" description="Shown to other users.">
      <Input />
    </Field>
  )
}

Attributes#

Attributes
data-requiredSlot: field-labelDescription: Present when the field input is required.
data-has-textSlot: field-containerDescription: Present when the control currently contains text.

Props#

Props for the Field component. Renders a <div> element by default.

Prop