Form field
Organizes form controls with labels, help text, error feedback, and semantic groups.
On this page
This docs is LLM-friendly and available as clean Markdown.
Supported browser agents can also use WebMCP to search, read, and open these docs. Learn more
Usage
import {
GlFormField,
GlFormFieldDescription,
GlFormFieldError,
GlFormFieldGroup,
GlFormFieldLabel,
GlFormFieldLegend,
GlFormFieldSet,
} from "gitlab-ui-react/form-field";
import { GlFormInput } from "gitlab-ui-react/form-input";<GlFormFieldSet>
<GlFormFieldLegend>Profile</GlFormFieldLegend>
<GlFormFieldGroup>
<GlFormField aria-labelledby="username-label">
<GlFormFieldLabel htmlFor="username" id="username-label">
Username
</GlFormFieldLabel>
<GlFormInput
aria-describedby="username-description username-error"
defaultValue="NriotHrreion"
id="username"
name="username"
state={false} />
<GlFormFieldDescription id="username-description">
Used in your profile URL.
</GlFormFieldDescription>
<GlFormFieldError id="username-error">
This username is already taken.
</GlFormFieldError>
</GlFormField>
</GlFormFieldGroup>
</GlFormFieldSet>Default
Use GlFormField to keep a control, its visible label, and help text together. Connect the label with htmlFor and id, and reference help text from the control with aria-describedby. The field supplies structure and styling; the consumer owns IDs, ARIA relationships, values, and validation.
import {
GlFormField,
GlFormFieldDescription,
GlFormFieldLabel,
} from "gitlab-ui-react/form-field";
import { GlFormInput } from "gitlab-ui-react/form-input";
export default function FormFieldExample() {
return (
<GlFormField aria-labelledby="field-username-label" className="max-w-lg">
<GlFormFieldLabel htmlFor="field-username" id="field-username-label">
Username
</GlFormFieldLabel>
<GlFormInput
aria-describedby="field-username-description"
defaultValue="NriotHrreion"
id="field-username"
name="username" />
<GlFormFieldDescription id="field-username-description">
Used in your profile URL.
</GlFormFieldDescription>
</GlFormField>
);
}
Multiple fields
Wrap adjacent fields in GlFormFieldGroup to stack them with consistent spacing. It provides layout and preserves child order. Add optional markers and supplementary label text explicitly inside GlFormFieldLabel; the optional-label and label-description classes provide their styling.
import {
GlFormField,
GlFormFieldDescription,
GlFormFieldGroup,
GlFormFieldLabel,
} from "gitlab-ui-react/form-field";
import { GlFormInput } from "gitlab-ui-react/form-input";
import { GlFormSelect, GlFormSelectItem } from "gitlab-ui-react/form-select";
export default function FormFieldGroupExample() {
return (
<GlFormFieldGroup className="max-w-lg">
<GlFormField aria-labelledby="field-profile-name-label">
<GlFormFieldLabel
htmlFor="field-profile-name"
id="field-profile-name-label">
Full name
</GlFormFieldLabel>
<GlFormInput id="field-profile-name" name="name" />
</GlFormField>
<GlFormField aria-labelledby="field-profile-username-label">
<GlFormFieldLabel
htmlFor="field-profile-username"
id="field-profile-username-label">
Username <span className="optional-label">(optional)</span>
<span className="label-description">Used in your profile URL.</span>
</GlFormFieldLabel>
<GlFormInput
aria-describedby="field-profile-username-description"
id="field-profile-username"
name="username" />
<GlFormFieldDescription id="field-profile-username-description">
You can change this later.
</GlFormFieldDescription>
</GlFormField>
<GlFormField aria-labelledby="field-profile-role-label">
<GlFormFieldLabel
htmlFor="field-profile-role"
id="field-profile-role-label">
Role
</GlFormFieldLabel>
<GlFormSelect
defaultValue="developer"
id="field-profile-role"
name="role">
<GlFormSelectItem value="developer">Developer</GlFormSelectItem>
<GlFormSelectItem value="maintainer">Maintainer</GlFormSelectItem>
</GlFormSelect>
</GlFormField>
</GlFormFieldGroup>
);
}
Fieldsets
Use GlFormFieldSet and GlFormFieldLegend to give related controls a shared semantic heading. Place the legend first, and keep an individual label for each text input or select. A description can apply to the fieldset through aria-describedby.
import {
GlFormField,
GlFormFieldDescription,
GlFormFieldGroup,
GlFormFieldLabel,
GlFormFieldLegend,
GlFormFieldSet,
} from "gitlab-ui-react/form-field";
import { GlFormInput } from "gitlab-ui-react/form-input";
export default function FormFieldSetExample() {
return (
<GlFormFieldSet
aria-describedby="field-address-description"
className="max-w-lg">
<GlFormFieldLegend>Address information</GlFormFieldLegend>
<GlFormFieldDescription
className="mb-4"
id="field-address-description">
Used for billing and account recovery.
</GlFormFieldDescription>
<GlFormFieldGroup>
<GlFormField aria-labelledby="field-street-label">
<GlFormFieldLabel htmlFor="field-street" id="field-street-label">
Street
</GlFormFieldLabel>
<GlFormInput id="field-street" name="street" />
</GlFormField>
<GlFormField aria-labelledby="field-city-label">
<GlFormFieldLabel htmlFor="field-city" id="field-city-label">
City
</GlFormFieldLabel>
<GlFormInput id="field-city" name="city" />
</GlFormField>
<GlFormField aria-labelledby="field-postal-code-label">
<GlFormFieldLabel
htmlFor="field-postal-code"
id="field-postal-code-label">
Postal code
</GlFormFieldLabel>
<GlFormInput id="field-postal-code" name="postalCode" />
</GlFormField>
</GlFormFieldGroup>
</GlFormFieldSet>
);
}
Multiple fieldsets
GlFormFieldGroup can also stack fieldsets, and each fieldset can contain its own field group. Use this structure to divide a longer form into understandable sections.
import { GlFormCheckbox } from "gitlab-ui-react/form-checkbox";
import {
GlFormField,
GlFormFieldGroup,
GlFormFieldLabel,
GlFormFieldLegend,
GlFormFieldSet,
} from "gitlab-ui-react/form-field";
import { GlFormInput } from "gitlab-ui-react/form-input";
import { GlFormSelect, GlFormSelectItem } from "gitlab-ui-react/form-select";
export default function FormFieldSetsExample() {
return (
<GlFormFieldGroup className="max-w-lg">
<GlFormFieldSet>
<GlFormFieldLegend>Account</GlFormFieldLegend>
<GlFormFieldGroup>
<GlFormField aria-labelledby="field-account-email-label">
<GlFormFieldLabel
htmlFor="field-account-email"
id="field-account-email-label">
Email
</GlFormFieldLabel>
<GlFormInput id="field-account-email" name="email" type="email" />
</GlFormField>
<GlFormField aria-labelledby="field-account-timezone-label">
<GlFormFieldLabel
htmlFor="field-account-timezone"
id="field-account-timezone-label">
Timezone
</GlFormFieldLabel>
<GlFormSelect
defaultValue="utc"
id="field-account-timezone"
name="timezone">
<GlFormSelectItem value="utc">UTC</GlFormSelectItem>
<GlFormSelectItem value="local">Local time</GlFormSelectItem>
</GlFormSelect>
</GlFormField>
</GlFormFieldGroup>
</GlFormFieldSet>
<GlFormFieldSet>
<GlFormFieldLegend>Notifications</GlFormFieldLegend>
<GlFormFieldGroup>
<GlFormField aria-label="Email notifications">
<GlFormCheckbox name="emailNotifications">
Email notifications
</GlFormCheckbox>
</GlFormField>
<GlFormField aria-label="Browser notifications">
<GlFormCheckbox name="browserNotifications">
Browser notifications
</GlFormCheckbox>
</GlFormField>
</GlFormFieldGroup>
</GlFormFieldSet>
</GlFormFieldGroup>
);
}
Choices and validation
Combine individual fields and fieldsets in the same layout. For related radio choices, use a legend for group context, retain each option’s label, and reference shared help text from each radio with aria-describedby. Apply invalid state to the control and associate GlFormFieldError through aria-describedby. Render errors only when needed; the error component displays its content whenever it is rendered and does not validate or announce changes automatically.
import {
GlFormField,
GlFormFieldDescription,
GlFormFieldError,
GlFormFieldGroup,
GlFormFieldLabel,
GlFormFieldLegend,
GlFormFieldSet,
} from "gitlab-ui-react/form-field";
import { GlFormInput } from "gitlab-ui-react/form-input";
import { GlFormRadio } from "gitlab-ui-react/form-radio";
import { GlFormRadioGroup } from "gitlab-ui-react/form-radio-group";
export default function FormFieldValidationExample() {
return (
<GlFormFieldGroup className="max-w-lg">
<GlFormField aria-labelledby="field-project-name-label">
<GlFormFieldLabel
htmlFor="field-project-name"
id="field-project-name-label">
Project name
</GlFormFieldLabel>
<GlFormInput
defaultValue="OPanel"
id="field-project-name"
name="projectName" />
</GlFormField>
<GlFormFieldSet>
<GlFormFieldLegend>Visibility</GlFormFieldLegend>
<GlFormFieldDescription
className="mb-4"
id="field-visibility-description">
Choose who can discover and view the project.
</GlFormFieldDescription>
<GlFormRadioGroup
aria-label="Visibility"
defaultValue="private"
name="field-visibility">
<GlFormRadio
aria-describedby="field-visibility-description"
value="private">
Private
</GlFormRadio>
<GlFormRadio
aria-describedby="field-visibility-description"
value="internal">
Internal
</GlFormRadio>
<GlFormRadio
aria-describedby="field-visibility-description"
value="public">
Public
</GlFormRadio>
</GlFormRadioGroup>
</GlFormFieldSet>
<GlFormField aria-labelledby="field-project-path-label">
<GlFormFieldLabel
htmlFor="field-project-path"
id="field-project-path-label">
Project path
</GlFormFieldLabel>
<GlFormInput
aria-describedby="field-project-path-error"
id="field-project-path"
name="projectPath"
state={false} />
<GlFormFieldError id="field-project-path-error">
This path is already in use.
</GlFormFieldError>
</GlFormField>
</GlFormFieldGroup>
);
}
Disabled fieldset
Set the native disabled attribute on GlFormFieldSet to disable its descendant native form controls. Keep the legend descriptive so the section remains understandable.
import { GlFormCheckbox } from "gitlab-ui-react/form-checkbox";
import {
GlFormField,
GlFormFieldGroup,
GlFormFieldLabel,
GlFormFieldLegend,
GlFormFieldSet,
} from "gitlab-ui-react/form-field";
import { GlFormInput } from "gitlab-ui-react/form-input";
export default function FormFieldDisabledExample() {
return (
<GlFormFieldSet className="max-w-lg" disabled>
<GlFormFieldLegend>Disabled preferences</GlFormFieldLegend>
<GlFormFieldGroup>
<GlFormField aria-labelledby="field-disabled-name-label">
<GlFormFieldLabel
htmlFor="field-disabled-name"
id="field-disabled-name-label">
Display name
</GlFormFieldLabel>
<GlFormInput
defaultValue="Norcleeh"
id="field-disabled-name"
name="displayName" />
</GlFormField>
<GlFormField aria-label="Product updates">
<GlFormCheckbox name="updates">Product updates</GlFormCheckbox>
</GlFormField>
</GlFormFieldGroup>
</GlFormFieldSet>
);
}
Accessibility
- Connect
GlFormFieldLabelto its control with matchinghtmlForandidvalues. Naming the surrounding field does not label the control. GlFormFieldalways rendersrole="group". Name it witharia-labelledbyoraria-labelwhen its context needs to be announced.GlFormFieldGroupis a layout-onlydivwith no default role.- Use
GlFormFieldLegendas the first child ofGlFormFieldSetto name related controls. Give each input or option its own accessible name. - Assign unique IDs to descriptions and errors, and reference the IDs from the relevant control using
aria-describedby. Include both IDs when help text and an error apply together. - Keep the control’s invalid state synchronized with visible error feedback. Choose an appropriate live region, such as
aria-live="polite", when new errors need to be announced. - Place optional and required information in visible label text, and set
requiredon the control when appropriate. A marker alone does not enforce validation.
API
These components accept children, supported native attributes for their rendered element, and a ref to that element. They do not generate controls, IDs, ARIA relationships, or validation state. Connect your form library’s value, change, blur, and ref bindings to the control, and render its feedback with these components.
GlFormField
Renders a div with role="group". Accepts native div attributes except role, which is fixed. Use aria-labelledby to reference the field’s label.
GlFormFieldGroup
Renders a div that stacks direct child fields and fieldsets with consistent spacing. Accepts native div attributes and preserves child order.
GlFormFieldSet
Renders a native fieldset. Accepts native fieldset attributes, including disabled, name, and form. Put a GlFormFieldLegend first to name the set.
GlFormFieldLegend
Renders a native legend with the field heading appearance. Accepts native legend attributes and labels its containing fieldset.
GlFormFieldLabel
Renders a native label. Accepts native label attributes, including htmlFor; set it to the control’s id. Optional markers and supplementary text are supplied as children.
GlFormFieldDescription
Renders a small element with help text styling. Accepts native HTML attributes; give it an id to reference from aria-describedby.
GlFormFieldError
Renders a div with error feedback styling. Accepts native div attributes, including id and aria-live. It is visible whenever rendered; manage its presence and announcements in the parent.