v0.5

Label

Caption for a form control, with slots for icons, markers, badges, tooltips, and counters.

Description

Label is a compound component that captions a form control. The root renders a real <label> (via Radix's Label.Root) when you pass htmlFor, and a neutral <span> when you don't. Around it sit optional parts: LabelIcon, LabelRequired, LabelOptional, LabelDescription, LabelBadgeSlot, LabelInfoSlot, and LabelCount, plus the LabelBlock / LabelMain layout wrappers for richer rows.

Reach for it whenever a control needs a name: inputs, textareas, selects, checkboxes, radios. Dropped inside a Field, the label reads size, invalid, and disabled straight from context, so a label restyles itself when the field goes invalid or disabled without any extra props. Use the slots to add a required asterisk, an optional tag, a help tooltip, an inline status badge, or a character counter aligned to the trailing edge.

Don't use Label for text that isn't tied to a control. For a clickable element, use Button. For the full field stack with helper text and error wiring, compose Field with FieldLabel, FieldDescription, and FieldError. For a section heading or standalone body copy, use Text.

Installation

pnpm dlx @create-ui/cli add label

Anatomy

<LabelBlock>
  <LabelMain>
    <Label htmlFor="…">
      <LabelIcon />
      <LabelRequired />
      <LabelOptional />
      <LabelBadgeSlot />
      <LabelInfoSlot />
    </Label>
    <LabelDescription />
  </LabelMain>
  <LabelCount />
</LabelBlock>

Usage

import { Label } from "@/components/ui/label"
<Label htmlFor="email">Email</Label>

Examples

Sizes

size accepts xs, sm, and md, controlling text and icon scale. The default is sm. Inside a Field, the label inherits the field's size, so set size on the Field and the label follows.

Required marker

LabelRequired renders a primary-colored asterisk after the label text. It defaults to *; pass children to override. It's decorative (aria-hidden), so still set required on the control itself.

Optional marker

LabelOptional adds a muted (Optional) tag for fields that aren't mandatory. Override the text by passing children.

With icon

LabelIcon renders a leading glyph sized to the active size. It's marked aria-hidden, so keep the meaning in the label text.

Info tooltip

LabelInfoSlot holds an InfoTooltip next to the label for inline help that doesn't need permanent space. The slot sizes the trigger to match the label.

Inline badge

LabelBadgeSlot keeps a Badge aligned with the label baseline. Use it for short status tags like NEW or UPDATED.

Character counter

LabelBlock arranges the label and a trailing LabelCount on one row, with LabelMain holding the label stack. Drive the count from your own state.

0/300

Description without a field

LabelDescription adds a secondary line under the label. Wrapped in LabelBlock / LabelMain, the whole group works standalone, with no surrounding Field.

Preferred editor

Used to pre-select your editor in tutorials and snippets.

All slots together

Every part composes in one row: leading icon, text, required marker, badge, info tooltip, a description line, and a trailing counter.

Used for sign-in and transactional email.

0/120

Accessibility

Label is not interactive. When htmlFor is set it renders a native <label> that moves focus to the associated control on click; focus and keyboard behavior belong to that control, not the label.

KeyDescription
-Not focusable; clicking focuses the associated control.

ARIA notes:

  • Pass htmlFor matching the control's id. The label then renders Radix's Label.Root (a native <label>) and becomes the control's accessible name.
  • With no htmlFor, the root renders a <span> and sets no association. Use that only when a parent (such as a checkbox row) wires the relationship, or when the label is purely visual.
  • LabelIcon, LabelRequired, and LabelOptional are decorative and marked aria-hidden. Keep the required state on the control (required) and the meaning in the label text.
  • Radix's accessibility behavior is documented in the Radix Label primitive.

Styling

Tailwind override: pass className to merge Tailwind classes with the component's classes (via cn()):

<Label className="tracking-wide uppercase">Email</Label>

Data slots and attributes: the component sets these for CSS targeting:

  • data-slot="label-block" on the LabelBlock wrapper, with data-size.
  • data-slot="label-main" on the LabelMain label-and-description stack.
  • data-slot="label" on the root label, with data-size.
  • data-slot="label-icon" on the LabelIcon wrapper, with data-size.
  • data-slot="label-required" on the required marker, with data-size.
  • data-slot="label-optional" on the optional marker, with data-size.
  • data-slot="label-description" on the description line, with data-size.
  • data-slot="label-badge-slot" on the badge slot.
  • data-slot="label-info-slot" on the info-tooltip slot, with data-size.
  • data-slot="label-count" on the counter, with data-size.

Inside a Field, the label also reacts to group-data-[invalid=true]/field and group-data-[disabled=true]/field. Target a specific size in CSS:

[data-slot="label"][data-size="md"] {
  /* … */
}
  • Field: wraps Label with description, error, and footer slots, and cascades size, invalid, and disabled to the label.
  • Input: the control a label most often captions; pair via matching htmlFor / id.
  • Textarea: multi-line control that pairs with LabelBlock plus LabelCount for character limits.
  • Checkbox: sits beside a Label in a horizontal field for opt-in rows.
  • InfoTooltip: the help affordance that lives inside LabelInfoSlot.
  • Text: use this for headings and body copy that aren't tied to a control.

API Reference

Label

The root caption. Renders Radix's Label.Root (a native <label>) when htmlFor is set, otherwise a <span>. Reads its size from an explicit prop, the nearest LabelBlock, or the surrounding Field. Extends React.ComponentProps<typeof LabelPrimitive.Root>, so standard label attributes are accepted.

Props

PropTypeDefaultDescription
htmlForstring-id of the associated control. When set, renders a native <label>.
size"xs" | "sm" | "md"-Overrides the inherited size. Falls back to LabelBlock, then Field, then sm.
classNamestring-Tailwind classes merged with the component's classes via cn().
childrenReact.ReactNode-Label text and any inline slots (LabelIcon, LabelRequired, etc.).

Variants

VariantOptionsDefaultDescription
size"xs" "sm" "md""sm"Type and icon scale. Cascades to every nested slot through context.

LabelBlock

Row wrapper that aligns a LabelMain stack with a trailing element such as LabelCount. Provides the size context for its children. Extends React.ComponentProps<"div">.

Props

PropTypeDefaultDescription
size"xs" | "sm" | "md"-Sets the size context for nested label parts. Falls back to Field, then sm.
asChildbooleanfalseRender as the child element via Radix Slot.
classNamestring-Tailwind classes merged with the component's classes via cn().
childrenReact.ReactNode-A LabelMain plus an optional trailing element.

LabelMain

Vertical stack that holds the Label and an optional LabelDescription. Extends React.ComponentProps<"div">.

Props

PropTypeDefaultDescription
classNamestring-Tailwind classes merged with the component's classes via cn().
childrenReact.ReactNode-A Label, optionally followed by a LabelDescription.

LabelIcon

Leading icon wrapper, sized to the active size and marked aria-hidden. Extends React.ComponentProps<"span">.

Props

PropTypeDefaultDescription
size"xs" | "sm" | "md"-Overrides the inherited size.
classNamestring-Tailwind classes merged with the component's classes via cn().
childrenReact.ReactNode-The icon to render.

LabelRequired

Required marker, defaulting to a primary-colored *. Decorative (aria-hidden). Extends React.ComponentProps<"span">.

Props

PropTypeDefaultDescription
size"xs" | "sm" | "md"-Overrides the inherited size.
classNamestring-Tailwind classes merged with the component's classes via cn().
childrenReact.ReactNode"*"Marker content.

LabelOptional

Optional marker, defaulting to a muted (Optional). Extends React.ComponentProps<"span">.

Props

PropTypeDefaultDescription
size"xs" | "sm" | "md"-Overrides the inherited size.
classNamestring-Tailwind classes merged with the component's classes via cn().
childrenReact.ReactNode"(Optional)"Marker content.

LabelDescription

Secondary line under the label for helper copy. Extends React.ComponentProps<"p">.

Props

PropTypeDefaultDescription
size"xs" | "sm" | "md"-Overrides the inherited size.
classNamestring-Tailwind classes merged with the component's classes via cn().
childrenReact.ReactNode-Helper text.

LabelBadgeSlot

Inline slot that keeps a Badge aligned with the label baseline. Extends React.ComponentProps<"span">.

Props

PropTypeDefaultDescription
classNamestring-Tailwind classes merged with the component's classes via cn().
childrenReact.ReactNode-A Badge or similar inline tag.

LabelInfoSlot

Inline slot for an InfoTooltip, sized smaller than LabelIcon. Extends React.ComponentProps<"span">.

Props

PropTypeDefaultDescription
size"xs" | "sm" | "md"-Overrides the inherited size.
classNamestring-Tailwind classes merged with the component's classes via cn().
childrenReact.ReactNode-An InfoTooltip or similar affordance.

LabelCount

Trailing counter for character or item limits. Extends React.ComponentProps<"span">.

Props

PropTypeDefaultDescription
size"xs" | "sm" | "md"-Overrides the inherited size.
classNamestring-Tailwind classes merged with the component's classes via cn().
childrenReact.ReactNode-The count text, for example 0/300.