Skip to main content
WireKit
Copy for LLM

Field

A wrapper that composes label, hint text, error message, and required indicator around any form input. Eliminates the boilerplate of wiring up for/id matching and aria-describedby by hand.

Before & After

Without the wrapper, every form field needs 5+ lines of manual composition:

<div class="space-y-1.5">
    <x-wirekit::label for="email" required>Email</x-wirekit::label>
    <x-wirekit::input name="email" type="email" id="email" />
    @error('email')<p class="...">{{ $message }}</p>@enderror
</div>

With the wrapper, it collapses to:

<x-wirekit::field label="Email" name="email" required>
    <x-wirekit::input name="email" type="email" />
</x-wirekit::field>

Basic Usage

Field with input

We will never share your email.

Required Fields

Required indicator

The red asterisk is marked aria-hidden="true" — screen readers rely on the required attribute on the input itself (pass it through as usual: <x-wirekit::input ... required />).

Error State

The wrapper automatically reads from Laravel's $errors bag when you pass a name, so after a failed submission the matching message renders below the input — no manual @error directive needed. You can also pass an explicit error prop for one-off messages (useful in demos, previews, or programmatic validation):

Own the error on ONE level. The form controls (input, select, textarea, checkbox, toggle) ALSO read the $errors bag by their own name and render their own message + red ring + aria-invalid. If you give the same name (or an explicit error) to BOTH the field and the control it wraps, the message renders twice. Pick one: give name/error to the control when you want its red ring and aria-invalid treatment (use the field for the label + hint only, without a name), or give it to the field when a plain message below the control is enough (leave the control's name/error off).

Field with an explicit error message

That email is already taken.

Works With Any Input

Select inside field
Textarea inside field

500 characters max.

Horizontal Orientation

Set orientation="horizontal" to place the label in a column beside the control instead of above it — the classic settings-form layout. The error/hint messages stay aligned under the control.

Horizontal field

Shown on your public profile.

Grouped Fields (Fieldset + Legend)

Wrap a set of related controls — most often a radio or checkbox group — in <x-wirekit::field.set>. It renders a native <fieldset> with a <legend>, the WCAG-recommended grouping pattern: the legend is announced before each control in the set. Pass legend (and optional hint) as props, or use <x-wirekit::field.legend> inside the slot for rich legend content.

Radio group inside a fieldset
Notification frequency

Choose how often we email you.

How It Works

The wrapper coordinates three pieces:

  1. Label — Rendered via <x-wirekit::label :for="$name" :required="$required">.
  2. Slot — Your input (any WireKit form component or plain HTML).
  3. Hint / Error — Rendered with stable IDs ({name}-hint, {name}-error) that individual inputs already reference via aria-describedby.

Design Decisions

  • Why does the child input still need its own name? The wrapper doesn't inject attributes into its slot — Blade slots are opaque. You pass name to both the field and the input. The wrapper uses it for the label's for attribute and for the $errors lookup; the input uses it for submission.
  • Why isn't there a size prop? Size is visual and belongs on the actual input (<x-wirekit::input size="lg">), not the wrapper.

Lining a Control Up With a Field

A toolbar is usually a field plus something beside it — a button, a second control. Put them in a row and the neighbor sits a label-height too high: the field's control starts below its label, and the neighbor starts at the top of the row.

items-end does not fix it, because it follows the field as the field grows. items-start does not either, because the first thing in the field's stack is the label, not the control.

<x-wirekit::field.spacer> puts a label-shaped blank where the label would go, so both columns begin with the same box and items-start lines the controls up.

Put it in a <x-wirekit::field> of its own. A field is what sets the distance between a label and the control under it, so a column that borrows the label's height has to borrow that distance too — otherwise the top edges match and everything below them is off by that gap. Use the field itself rather than a stack with a chosen gap: it is the same spacing, from the component that defines it, and it stays right when that spacing changes.

A button that starts where the input starts

More than one control goes in a row inside that same field, so the group still starts on the input's line:

Two actions beside a field

It renders the real <x-wirekit::label>, not a hand-written stand-in with copied classes — so it stays exactly as tall as a label when the typography changes. A copy would drift, and the drift would look like a small misalignment rather than like a stale duplicate.

It is aria-hidden: a screen reader announcing an empty label would be describing a layout decision as if it were content.

This is the other half of reserve-message. That one holds the space below a control so a field does not shove its neighbors down when validation fires; this one holds the space above so two columns start on the same line.

Props

Prop Type Default Description
label string|null null Label text (omit to skip label rendering)
name string|null null Used for for="{name}" AND $errors->has(name) lookup
hint string|null null Helper text below the input
error string|null null Explicit error message (overrides $errors bag)
announceError bool true Render the error as an ARIA live region (aria-live="polite") so a dynamically appearing validation error is announced to screen readers. Set false when the page runs its own live region
required bool false Renders red asterisk next to label
for string|null null Custom for target (defaults to name)
orientation string 'vertical' vertical (label above) or horizontal (label beside the control)
scope string|null null Scoped personalization name

<x-wirekit::field.set> props

Prop Type Default Description
legend string|null null Group label rendered as <legend> (or use <x-wirekit::field.legend> in the slot)
hint string|null null Optional helper text below the legend
scope string|null null Scoped personalization name

<x-wirekit::field.spacer> props

Prop Type Default Description
scope string|null null Scoped personalization name

Accessibility

  • for / id matching is automatic — clicking the label always focuses the input.
  • Error messages are linked via aria-describedby on the inner input (each WireKit form component does this independently).
  • The error message is an ARIA live region (aria-live="polite") by default, so a validation error that appears after a Livewire round-trip is announced without the focus returning to the field. Disable it with :announce-error="false" when the page runs its own live region.
  • Required indicator is aria-hidden — the semantic required attribute on the input itself is what screen readers announce.

Keyboard Interaction

This component is a layout wrapper. Keyboard interaction is delegated to its children.

Design Tokens

The Field wrapper is layout-only — it composes Label above the slot and renders hint / error text using the same tokens as Input. Visual styling on the input itself comes from whichever WireKit form component you slot in.

Token Used for
--font-wk-sans Hint + error font family
--text-wk-md Hint + error font size
--color-wk-text Body text
--color-wk-danger-text Error message

See Also

  • Inline Edit — edit this value in place, with an explicit confirm step

Further Reading

Was this page helpful?

Voting requires cookies or local storage. What we store