HextaUI

Label

A label that follows its control, dimming when it's disabled and marking it required or optional on its own.

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

export function LabelDemo() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-6">
      <div className="flex flex-col gap-2">
        <Label htmlFor="label-demo-email" indicator="required">
          Email
        </Label>
        <Input
          id="label-demo-email"
          type="email"
          autoComplete="email"
          required
        />
      </div>
      <div className="flex flex-col gap-2">
        <Label htmlFor="label-demo-team" indicator="required">
          Team
        </Label>
        <Input id="label-demo-team" defaultValue="Design systems" disabled />
      </div>
      <Label>
        <Checkbox defaultChecked />
        Send me product updates
      </Label>
    </div>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/label.json

Adds the component, the HextaUI theme tokens and any HextaUI components it depends on.

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

Point htmlFor at a control's id, or wrap the control. Either way the label finds it and follows its state. Inside a <Field />, use <FieldLabel />, which wires up the id for you.

Follows its control

The label dims and shows a not-allowed cursor while its control is disabled, and exposes data-required, data-invalid and data-readonly for your own styles. It keeps up when the control changes.

"use client"

import * as React from "react"

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

export function LabelStates() {
  const [locked, setLocked] = React.useState(true)

  return (
    <div className="flex w-full max-w-sm flex-col gap-3">
      <div className="flex flex-col gap-2">
        <Label htmlFor="label-states-domain">Custom domain</Label>
        <Input
          id="label-states-domain"
          defaultValue="docs.example.com"
          disabled={locked}
        />
      </div>
      <Button
        variant="outline"
        size="sm"
        className="self-start"
        onClick={() => setLocked(!locked)}
      >
        {locked ? "Unlock" : "Lock"}
      </Button>
    </div>
  )
}

Required and optional

indicator="optional" tags controls without required, and indicator="required" adds an asterisk to controls with it. The mark is hidden from screen readers, which already announce required fields.

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

export function LabelIndicator() {
  return (
    <div className="grid w-full max-w-sm gap-6">
      <div className="flex flex-col gap-2">
        <Label htmlFor="label-indicator-name" indicator="optional">
          Full name
        </Label>
        <Input id="label-indicator-name" autoComplete="name" required />
      </div>
      <div className="flex flex-col gap-2">
        <Label htmlFor="label-indicator-company" indicator="optional">
          Company
        </Label>
        <Input id="label-indicator-company" autoComplete="organization" />
      </div>
      <div className="flex flex-col gap-2">
        <Label htmlFor="label-indicator-card" indicator="required">
          Card number
        </Label>
        <Input id="label-indicator-card" inputMode="numeric" required />
      </div>
    </div>
  )
}

Checkbox

Wrap a checkbox so the whole label toggles it, or place the label beside it with htmlFor. A disabled checkbox dims its label either way.

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

export function LabelCheckbox() {
  return (
    <div className="flex flex-col gap-4">
      <Label>
        <Checkbox defaultChecked />
        Email me when someone replies
      </Label>
      <Label>
        <Checkbox disabled />
        Weekly digest (coming soon)
      </Label>
      <div className="flex items-center gap-2">
        <Checkbox id="label-checkbox-terms" />
        <Label htmlFor="label-checkbox-terms">Accept the terms</Label>
      </div>
    </div>
  )
}

Icon

Icons inside a label are sized and muted to sit next to the text.

import { IconLock } from "@tabler/icons-react"

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

export function LabelIcon() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-2">
      <Label htmlFor="label-icon-key">
        <IconLock aria-hidden="true" />
        API key
      </Label>
      <Input id="label-icon-key" defaultValue="sk_live_51H…" readOnly />
    </div>
  )
}

Long content

Long labels wrap, and unbroken strings break instead of widening the layout.

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

export function LabelLongContent() {
  return (
    <div className="flex w-64 max-w-full flex-col gap-6">
      <div className="flex flex-col gap-2">
        <Label htmlFor="label-long" indicator="optional">
          Where should we send invoices for the international subsidiary
        </Label>
        <Input id="label-long" />
      </div>
      <Label>
        <Checkbox />
        billing-notifications@an-extremely-long-company-domain.example.com
      </Label>
    </div>
  )
}

Right to left

Gaps and the indicator follow the reading direction.

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

export function LabelRtl() {
  return (
    <div dir="rtl" className="flex w-full max-w-sm flex-col gap-6">
      <div className="flex flex-col gap-2">
        <Label
          htmlFor="label-rtl-name"
          indicator="optional"
          optionalText="اختياري"
        >
          الاسم
        </Label>
        <Input id="label-rtl-name" />
      </div>
      <Label>
        <Checkbox defaultChecked />
        تذكرني
      </Label>
    </div>
  )
}
  • Clicking the label focuses or toggles its control, so it's a bigger target than the control alone.
  • Double-clicking the label text doesn't select it. Double-clicks on a control inside the label work as usual.
  • Every control needs a name. When there's no visible label, use aria-label on the control instead.

Renders a <label> and accepts its attributes.

PropTypeDefault
htmlForThe id of the control. Leave it out when the label wraps the control.
string–
indicatorMark the label from the control's required state. Off by default.
"required" | "optional"–
optionalText
ReactNode"Optional"
render
ReactElement | (props, state) => ReactElement<label>
AttributeDescription
data-slot="label"Target labels in CSS.
data-disabledPresent while the control is disabled.
data-requiredPresent while the control is required.
data-invalidPresent while the control is invalid, after the user has interacted or when aria-invalid is set.
data-readonlyPresent while the control is read-only.
data-slot="label-indicator"The required or optional mark.