Skip to content

Label

The accessible name of a form control. Clicking it focuses or toggles the control it belongs to.

SourceRegistry
import { Checkbox } from "@/components/ui/checkbox"
import { Label } from "@/components/ui/label"

export default function LabelDemo() {
  return (
    <div className="flex items-center gap-2.5">
      <Checkbox id="label-demo-terms" defaultChecked />
      <Label htmlFor="label-demo-terms">
        Email me when someone mentions me
      </Label>
    </div>
  )
}

Installation

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/label.json

Usage

import { Label } from "@/components/ui/label"
<Label htmlFor="display-name">Display name</Label>
<Input id="display-name" />
Building a form? Use FieldLabel.
Label is the primitive. Inside forms, reach for Field and its FieldLabel, which adds consistent spacing, disabled states and choice-card styling on top of it.

Examples

With an input

A label above its input is the fastest layout to scan and the most robust on narrow screens.

import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"

export default function LabelWithInput() {
  return (
    <div className="flex w-72 flex-col gap-2">
      <Label htmlFor="label-with-input">Display name</Label>
      <Input id="label-with-input" defaultValue="Ada Lovelace" />
    </div>
  )
}

Optional fields

When most fields in a form are required, mark the exceptions as optional instead of decorating every other label with an asterisk. The hint sits inside the label, so it is announced with the field's name.

import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"

export default function LabelOptional() {
  return (
    <div className="flex w-full max-w-md flex-col gap-5">
      <div className="flex flex-col gap-2">
        <Label htmlFor="label-optional-name">Full name</Label>
        <Input id="label-optional-name" required autoComplete="name" />
      </div>
      <div className="flex flex-col gap-2">
        <Label htmlFor="label-optional-company">
          Company
          <span className="font-normal text-muted-foreground">(optional)</span>
        </Label>
        <Input id="label-optional-company" autoComplete="organization" />
      </div>
    </div>
  )
}

Disabled controls

A label that follows its control in the DOM dims when the control is disabled. The label listens to a preceding sibling with the peer class: Base UI controls such as Checkbox and Switch carry it and report the state through data-disabled. A native control is matched through :disabled, but only after you add className="peer" to it; Input doesn't carry peer. A label placed above its control can't use a sibling selector, so there use a FieldLabel inside a Field with data-disabled.

import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch"

export default function LabelDisabled() {
  return (
    <div className="flex flex-col gap-4">
      <div className="flex items-center gap-2.5">
        <Switch id="label-disabled-sync" defaultChecked />
        <Label htmlFor="label-disabled-sync">Sync across devices</Label>
      </div>
      <div className="flex items-center gap-2.5">
        <Switch id="label-disabled-beta" disabled />
        <Label htmlFor="label-disabled-beta">Beta features (admins only)</Label>
      </div>
    </div>
  )
}

Guidelines

When to use

  • To name any form control: inputs, textareas, selects, checkboxes, radios and switches.
  • Standalone, outside a Field, for small compositions such as a single checkbox with its text.

When not to use

  • For headings of a group of controls — use a FieldLegend inside a FieldSet, which screen readers announce as the group's name.
  • As a clickable link or button — labels only focus or toggle their control.

Writing labels

Name the thing, not the action: Email, Billing address, Team size. Use sentence case, keep it to a few words, and skip trailing colons — the visual relationship already does that job. Put instructions in a FieldDescription, not in the label.

DoShort, sentence-case nouns. Details go below the field.
Don’tUppercase, colons and instructions crammed into the label slow people down.

Accessibility

  • Associate explicitly. Set htmlFor to the control's id. Wrapping the control inside the label also works, but explicit association is more robust across assistive technologies.
  • A bigger target. Clicking a label focuses its input or toggles its checkbox, radio or switch — a larger hit area that helps everyone, and is essential for small controls (WCAG 2.5.8).
  • One name per control. Don't combine a visible label with a different aria-label; the visible text must be part of the accessible name (WCAG 2.5.3).
  • Contrast. Labels use foreground at 14px medium — well above 4.5:1 in both modes.

API reference

Label

Renders a native <label> element and accepts all of its attributes.

PropTypeDefault
htmlFor

The id of the control this label names.

stringNo default
className

Merged with prfct's label styles through cn().

stringNo default