Skip to main content
Urbicon UI

FormField

Layout wrapper for composite form fields that need a label, helper text, and error message but cannot rely on the built-in slots of Input/Select/Textarea.

Playground

PDF, JPG, PNG — max 10 MB
<FormField
  error=""
  helper="PDF, JPG, PNG — max 10 MB"
  label="Document"
>
  {#snippet children(ctx)}
    <!--
      Das Kind ist ein `<Input>`, kein rohes `<input>`: FormField reicht
      `id`/`describedBy`/`invalid`/`required` an ein *beliebiges* Control
      durch, und genau diesen Vertrag soll das Beispiel zeigen — aber im
      eigenen Vokabular. Vorher stand hier ein `<input type="file">` mit
      acht handgeschriebenen Tailwind-Klassen; wer den Schnipsel kopierte,
      kopierte eine Anleitung, an der Bibliothek vorbeizubauen (und
      ausgerechnet für den Fall, für den es `FileUpload` gibt).

      Kein eigenes `label` am Input: Das trägt der FormField, sonst steht
      es doppelt.
    -->
    <Input
      id={ctx.id}
      placeholder="Invoice 2026-07.pdf"
      aria-describedby={ctx.describedBy}
      aria-invalid={ctx.invalid}
      required={ctx.required}
    />
  {/snippet}
</FormField>

01 Examples

Wrapping a custom file input

The slot receives wiring metadata (id, describedBy, invalid, required, disabled). Spread or apply these so screen readers can find the label and any error/helper text.
PDF, JPG, PNG — max 10 MB
<FormField label="Document" required helper="PDF, JPG, PNG — max 10 MB">
  {#snippet children({ id, describedBy, invalid, required })}
    <input
      {id}
      type="file"
      aria-describedby={describedBy}
      aria-invalid={invalid}
      {required}
    />
  {/snippet}
</FormField>

With error

<FormField label="Document" error="Required" required>
  {#snippet children({ id, describedBy, invalid })}
    <input {id} type="file" aria-describedby={describedBy} aria-invalid={invalid} />
  {/snippet}
</FormField>

02 Customization

Built-in form primitives (Input, Select, Textarea) already render their own label + helper + error — FormField is not needed there. Use FormField only for composite controls that don't have those slots, e.g. FileUpload, custom number-spinner combinations, or media uploaders.

FormField is a documented deviation from the standard styling contract: as a bare layout wrapper without a tv() config it has no unstyled mode and no preset support. Restyle it via class (the wrapper element) and slotClasses with the hand-maintained keys wrapper, label, message, and helper — e.g. a bolder label via the label key. The wrapped control keeps its own styling API. See Customization for the general contract.

03 Accessibility

Label association

The wrapper renders a <label for=…> linked to the slot's id.

Description and error share one slot

Helper text gets an id referenced via aria-describedby. When an error is present the error message takes the spot and the helper is hidden — this matches WCAG guidance to surface the most actionable message to AT users.

Errors are announced

The error renders with role="alert", so it is announced when the value changes during validation.

04 API Reference

10 props
10 props 1 required
Prop
Type
Default
Description

05 Types

Local type definitions used by this component.

2 types
Name
Kind
Category
Used by
Description

06 Installation

Import

import { FormField } from '@urbicon-ui/blocks';