Skip to content

Now in the official shadcn registry directory.

Start free

Label

An accessible form label built on radix-ui that auto-dims when the associated control is disabled.

form
A free styled version is available

Installation

Run the following command

npx shadcn@latest add @ai2/label

Dependencies, the @ai2/tokens theme and the component file are installed together.

Usage

TS
import { Label } from "@/components/ui/label"

<Label htmlFor="email">Work email</Label>

Pair it with any form control via htmlFor, or wrap the control directly. Both associate the click target with the control.

Examples

With input

Wrapping a control

The label lays out as a flex row with a built-in gap, so an inline checkbox needs no extra wrapper.

Sizes

size="sm" drops the text from text-sm to text-xs for dense rows and secondary labels; md is the default.

Disabled control

When the label follows a disabled peer control (ai2 checkboxes ship the peer class) or sits inside a group with data-disabled="true", it dims automatically, with no extra classes on the label itself.

Props

Label adds a single size prop. It also accepts every prop of the radix-ui Label primitive, which itself forwards all native <label> props such as htmlFor.

PropTypeDefaultDescription
size"sm" | "md""md"Text size: md renders text-sm, sm renders text-xs for dense rows and secondary labels.

ai2 Label: accessible form labels for React, built on radix-ui

The ai2 Label is a shadcn-compatible label component for React, built on the radix-ui Label primitive and styled with Tailwind CSS v4 on the shared ai2 tokens. It names form controls the way HTML intends, with htmlFor association or by wrapping the control, and adds one thing most hand-rolled labels miss: it dims itself automatically when the control it belongs to is disabled.

Because it ships through the shadcn registry format, you install it with one CLI command, an MCP agent, or a copy-paste, and the source lands in your own project. You own the file; there is no runtime dependency on ai2 itself. The live example above is the exact component you get.

What is the ai2 Label?

It is a single part: one Label component wrapping LabelPrimitive.Root from radix-ui. The primitive renders a native <label> element and prevents the double-click text selection that plain labels suffer from, while keeping every native prop like htmlFor.

The styling makes it a flex row with a built-in gap, so a label that wraps a checkbox or switch lays out correctly with no extra wrapper. A size prop picks the text size: md (default) renders text-sm, sm renders text-xs for dense rows. Disabled awareness is CSS-only: Tailwind peer-disabled selectors react to a disabled sibling control, and group-data-disabled selectors react to a parent marked disabled.

Why use it

  • Real label semantics: Built on the radix-ui Label primitive, which renders a native label element: clicking it focuses or toggles the associated control, for screen readers and everyone else.
  • Auto-dims for disabled controls: peer-disabled styles mute the label and switch the cursor to not-allowed when the sibling control is disabled. The state stays visually in sync with zero JavaScript.
  • Group-level disabling too: Inside a container with the group class and data-disabled="true", the label dims and stops receiving pointer events, covering composite fields.
  • Wraps controls cleanly: The flex row layout with a built-in gap means <Label><Checkbox /> Accept terms</Label> aligns correctly without a wrapper div.
  • Agent-readable metadata: The registry item describes the association patterns in plain words, so an MCP agent can wire labels to controls without guessing.

Features

  • shadcn registry install: One command adds the component, the radix-ui dependency and the @ai2/tokens theme to your project.
  • radix-ui Label primitive: Native label rendering plus primitive niceties like guarding against accidental text selection on double click.
  • Two association modes: Point at a control with htmlFor and a matching id, or wrap the control as a child; both make the label a click target for it.
  • CSS-only disabled sync: peer-disabled and group-data-[disabled=true] selectors handle the muted state; no state props or context providers involved.
  • Data attribute for styling: The element exposes data-slot="label", so you can adjust every form label from one CSS rule without forking.
  • TypeScript source: The installed file is typed end to end and forwards the full radix Label API, so autocomplete covers everything including htmlFor.

Production tips

  • Prefer htmlFor for text inputs: Explicit htmlFor and id association keeps the DOM flat and works with every form library. Save the wrapping pattern for checkboxes and switches.
  • Lean on peer for disabled states: ai2 controls ship the peer class, so a Label placed after a disabled control dims automatically. Keep the label after the control in the DOM for peer selectors to apply.
  • Do not replace labels with placeholders: Placeholders vanish on input and are not accessible names. Every input should keep a visible Label even when the design looks minimal.
  • Keep label text short and specific: One or two words like "Work email" beat sentences. Longer guidance belongs in a FieldDescription below the control.
  • Use Field for full anatomy: When you need description and error text too, reach for the ai2 Field composition; its FieldLabel is this same Label with the layout handled.

Works with the rest of ai2

Label pairs with every form control in the registry: an ai2 Input via htmlFor, a wrapped ai2 Checkbox or ai2 Switch for inline consent rows, and items inside an ai2 Radio Group.

For complete form rows, the Field composition builds on this exact Label and adds description and error placement. One token source keeps typography and disabled states consistent across every form in both modes.

FAQs

Get new components in your inbox

Component drops, new themes and release notes. No spam, and you can unsubscribe from any email.

© 2026 ai2 design system · MIT licensed · Built with its own components