Skip to content

Field Group

Field Group components are used as a wrapper component to create a consistent look and feel for form inputs and to provide a consistent way to handle error messages and helper text. It is suggest that this component be baked into your normal form input component.

Instruction content goes here lorem ipsum dolor sit.
<FieldGroup
label="Label"
name="example-1"
sublabel="Sub-label content here lorem ipsum"
instructions="Instruction content goes here lorem ipsum dolor sit."
>
<div>Slot</div>
</FieldGroup>

API Reference

This component is based on the <div> element and can take in any additional HTML attributes. The following are the custom props that can be passed to the component.

Prop Type Default
name *
string
label *
string
labelHidden
boolean false
sublabel
string
instructions
string
required
boolean | "long" false
errorMessages
string[]

Accessibility

The <FieldGroup> component includes some accessibility features to help ensure that the component is usable and understandable by all users. The following are some of the key accessibility features that are included in the component and worth considering:

  • The label element is associated with the input field by using the for attribute and includes any sublabel text.
  • The label and instructions elements are associated with the input field by using the aria-labelledby attribute. This is an important consideration specifically for the instructions so that they are read out by screen readers when the field is focused, rather than after the input field.
  • The errorMessages are displayed below the input field and are marked up as a list of error messages.
  • The labelHidden prop visually hides the label but still ensures it is available to screen readers.
  • The required prop marks the input field as required and can include explicit “required” text if the value is set to "long".

Examples

Required

Use the required prop to mark a field as required. You can also use the required prop to mark a field as required and include explicit “required” text by passing the value "long".

<FieldGroup label="Label" name="example-2" required>
<div>Slot</div>
</FieldGroup>
<FieldGroup label="Label" name="example-3" required="long">
<div>Slot</div>
</FieldGroup>

With Helper Text

Use the sublabel and instructions props to add helper text to the input field.

Instructions lorem ipsum dolor.
<FieldGroup
label="Label"
name="example-4"
sublabel="Sublabel lorem ipsum dolor sit amet."
instructions="Instructions lorem ipsum dolor."
>
<div>Slot</div>
</FieldGroup>

With Errors

Use the errorMessages prop to display error messages below the input field.

  • Error message 1
  • Error message 2
<FieldGroup
label="Label"
name="example-5"
errorMessages={['Error message 1', 'Error message 2']}
>
<div>Slot</div>
</FieldGroup>

With Hidden Label

Use the labelHidden prop to visually hide the label. When hidden, the label will still be available to screen readers.

<FieldGroup label="Label" name="example-6" labelHidden>
<div>Slot</div>
</FieldGroup>

Astro Component

It’s recommended that an element like this is made as a reusable component. In this case, we have put together the following Astro component to serve as a blueprint for how you could possibly set up a field group component, and what properties to consider.

FieldGroup.astro
---
import type { HTMLAttributes } from 'astro/types'
interface Props extends HTMLAttributes<'div'> {
/**
Unique name of the input field. Should follow a kebab-case naming convention (e.g. "first-name")
*/
name: string
/**
Visual or assistive label for the input field
*/
label: string
/**
Whether to visually hide the label or not. When hidden, the label will still be available to screen readers
*/
labelHidden?: boolean
/**
Sublabel for the input field
*/
sublabel?: string
/**
Instructions for the input field that will be placed below the input
*/
instructions?: string
/**
Whether the input field is required or not. If set to 'long', the input field will be marked as required and include explicit "required" text.
*/
required?: boolean | 'long'
/**
Error messages to display below the input field
*/
errorMessages?: string[]
}
const {
name,
label,
labelHidden = false,
sublabel,
instructions,
required = false,
errorMessages = [],
class: className,
...attrs
} = Astro.props
---
<div
class:list={['field-group', className]}
data-required={required}
data-state={errorMessages.length > 0 ? 'error' : null}
data-layout="FieldGroup"
{...attrs}
>
<label for={name} id={`label-${name}`}>
<span class:list={['field-label', { 'sr-only': labelHidden }]}>{label}</span
>
{sublabel && <span class="field-sublabel">{sublabel}</span>}
</label>
<slot />
{
instructions && (
<span class="field-instructions" id={`instructions-${name}`}>
{instructions}
</span>
)
}
{
errorMessages.length > 0 && (
<ul class="field-errors">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="absolute left-0 top-2 size-16 shrink-0"
>
<line x1="12" y1="9" x2="12" y2="13" />
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
<line x1="12" y1="17" x2="12.01" y2="17" />
</svg>
{errorMessages.map((message) => (
<li>{message}</li>
))}
</ul>
)
}
</div>