Installation pnpm dlx shadcn@latest add https://rdf.dev.360online.com/r/label.jsonInstalls to @/components/ui/label.
Manual installation Copy the source into your project:
components/ui/label.tsx 'use client'
import type * as React from 'react'
import { cn } from '@/lib/utils'
function Label ({ className , ... props } : React . ComponentProps < 'label' >) {
return (
// biome-ignore lint/a11y/noLabelWithoutControl: the control is associated by the consumer via htmlFor or nesting
< label
data-slot = "label"
className = { cn (
'flex items-center gap-2 text-sm leading-none font-medium text-foreground select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50' ,
className,
)}
{ ... props}
/>
)
}
export { Label } A label component that renders a native label element, providing an accessible way to associate text labels with form controls.
Features
A plain native label element (no primitive dependency)
Accessible association with form controls via htmlFor
Laid out as flex items-center gap-2, so icons and badges line up inline
Dims automatically after a disabled peer control (peer-disabled)
Dims inside a group with data-disabled="true" (as used by Field)
Required field indication via className
Usage
import { Label } from '@/components/ui/label'
import { Input } from '@/components/ui/input'
import { Checkbox } from '@/components/ui/checkbox'
// Basic usage
< Label >Label text</ Label >
// With input
< div className = "grid gap-1.5" >
< Label htmlFor = "email" >Email</ Label >
< Input id = "email" type = "email" />
</ div >
// With checkbox
< div className = "flex items-center space-x-2" >
< Checkbox id = "terms" />
< Label htmlFor = "terms" >Accept terms</ Label >
</ div >
// Required field
< Label htmlFor = "email" className = "after:text-destructive after:content-['*']" >
Email
</ Label >
// Disabled state (group)
< div className = "group grid gap-1.5" data-disabled = "true" >
< Label htmlFor = "name" >Name</ Label >
< Input id = "name" disabled />
</ div >
// Disabled state (peer)
< div className = "flex items-center gap-2" >
< Checkbox id = "terms" className = "peer" disabled />
< Label htmlFor = "terms" >Accept terms</ Label >
</ div >
Examples
Default
A label associated with an input through htmlFor.
Labels on an address block.
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
export function LabelWithInput () {
return (
< div className = "grid w-full max-w-sm grid-cols-2 gap-x-3 gap-y-4" >
< div className = "col-span-2 grid gap-2" >
< Label htmlFor = "label-street" >Street address</ Label >
< Input id = "label-street" autoComplete = "street-address" defaultValue = "Kungsgatan 3" />
</ div >
< div className = "grid gap-2" >
< Label htmlFor = "label-postcode" >Postal code</ Label >
< Input id = "label-postcode" autoComplete = "postal-code" defaultValue = "111 43" />
</ div >
< div className = "grid gap-2" >
< Label htmlFor = "label-city" >City</ Label >
< Input id = "label-city" autoComplete = "address-level2" defaultValue = "Stockholm" />
</ div >
</ div >
)
} View Code
With Checkbox
A label next to a checkbox, with and without a hint below it.
import { Checkbox } from '@/components/ui/checkbox'
import { Label } from '@/components/ui/label'
export function LabelWithCheckbox () {
return (
< div className = "flex w-full max-w-sm flex-col gap-4" >
< div className = "flex items-center gap-3" >
< Checkbox id = "label-terms" defaultChecked />
< Label htmlFor = "label-terms" >I have read the information about how my data is processed</ Label >
</ div >
< div className = "flex items-start gap-3" >
< Checkbox id = "label-sms" className = "mt-0.5" />
< div className = "grid gap-1.5" >
< Label htmlFor = "label-sms" >Send me SMS reminders</ Label >
< p className = "text-sm text-muted-foreground" >One day before a home help visit is rescheduled.</ p >
</ div >
</ div >
</ div >
)
} View Code
With Switch
A label and a hint for a switch in a settings row.
import { Switch } from '@/components/ui/switch'
import { Label } from '@/components/ui/label'
export function LabelWithSwitch () {
return (
< div className = "flex w-full max-w-sm items-center justify-between gap-4 rounded-lg border p-3" >
< div className = "grid gap-1" >
< Label htmlFor = "label-digital-mail" >Digital mailbox</ Label >
< p className = "text-xs text-muted-foreground" >Receive decisions digitally instead of by post.</ p >
</ div >
< Switch id = "label-digital-mail" defaultChecked />
</ div >
)
} View Code
With Radio Group
One label names the group (via aria-labelledby), and each option has its own label.
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
import { Label } from '@/components/ui/label'
export function LabelWithRadioGroup () {
return (
< div className = "grid w-full max-w-sm gap-3" >
< Label id = "label-visit-time" >Preferred visit time</ Label >
< RadioGroup defaultValue = "morning" aria-labelledby = "label-visit-time" >
< div className = "flex items-center gap-3" >
< RadioGroupItem value = "morning" id = "label-visit-morning" />
< Label htmlFor = "label-visit-morning" className = "font-normal" >
Morning, 08:00–11:00
</ Label >
</ div >
< div className = "flex items-center gap-3" >
< RadioGroupItem value = "afternoon" id = "label-visit-afternoon" />
< Label htmlFor = "label-visit-afternoon" className = "font-normal" >
Afternoon, 13:00–16:00
</ Label >
</ div >
</ RadioGroup >
</ div >
)
} View Code
Required Field
Mark required fields with a className, or mark the optional ones in text.
import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea'
import { Label } from '@/components/ui/label'
export function LabelRequired () {
return (
< div className = "grid w-full max-w-sm gap-4" >
< div className = "grid gap-2" >
< Label htmlFor = "label-required-email" className = "after:-ml-1 after:text-destructive after:content-['*']" >
Email
</ Label >
< Input id = "label-required-email" type = "email" autoComplete = "email" required aria-required = "true" />
</ div >
< div className = "grid gap-2" >
< Label htmlFor = "label-optional-message" >
Message < span className = "font-normal text-muted-foreground" >(optional)</ span >
</ Label >
< Textarea id = "label-optional-message" rows = { 3 } />
</ div >
</ div >
)
} View Code
Disabled State
A label inside a group with data-disabled="true".
import { LockIcon } from 'lucide-react'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
export function LabelDisabled () {
return (
< div className = "group grid w-full max-w-sm gap-2" data-disabled = "true" >
< Label htmlFor = "label-disabled-input" >
Personal identity number
< LockIcon className = "size-3.5" aria-hidden = "true" />
</ Label >
< Input id = "label-disabled-input" defaultValue = "19850312-••••" disabled />
</ div >
)
} View Code
Peer Disabled
A label after a disabled peer control.
import { Checkbox } from '@/components/ui/checkbox'
import { Label } from '@/components/ui/label'
export function LabelPeerDisabled () {
return (
< div className = "flex items-center gap-3" >
< Checkbox id = "label-peer-disabled" className = "peer" disabled />
< Label htmlFor = "label-peer-disabled" >Priority for siblings (not available for upper secondary)</ Label >
</ div >
)
} View Code
With Icon
Badges and an info tooltip lined up next to the label.
import { InfoIcon } from 'lucide-react'
import { Badge } from '@/components/ui/badge'
import { Input } from '@/components/ui/input'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import { Label } from '@/components/ui/label'
export function LabelWithIcon () {
return (
< TooltipProvider >
< div className = "grid w-full max-w-sm gap-2" >
< div className = "flex items-center gap-2" >
< Label htmlFor = "label-property" >
Property designation
< Badge variant = "secondary" >New</ Badge >
</ Label >
< Tooltip >
< TooltipTrigger
render = {
< button
type = "button"
className = "text-muted-foreground outline-none hover:text-foreground focus-visible:ring-3 focus-visible:ring-ring rounded-sm"
aria-label = "What is a property designation?"
/>
}
>
< InfoIcon className = "size-4" />
</ TooltipTrigger >
< TooltipContent >The name and number of the property, e.g. Björken 4:12.</ TooltipContent >
</ Tooltip >
</ div >
< Input id = "label-property" placeholder = "Björken 4:12" />
</ div >
</ TooltipProvider >
)
} View Code
Props
Label accepts all native label attributes, including:
htmlFor: ID of the associated form control
className: Additional CSS classes
It has no custom variants.
Accessibility
The Label component follows WAI-ARIA guidelines and best practices:
Uses native HTML label element
Provides proper form control association
Maintains focus management
Supports screen readers
Communicates required and disabled states
Ensures proper color contrast
Best Practices
Association:
Always associate labels with form controls
Use consistent IDs for form elements
Maintain clear relationships
Content:
Use clear, concise text
Include units when necessary
Indicate required fields consistently
Consider internationalization
Styling:
Maintain sufficient contrast
Use appropriate text size
Consider mobile viewports
Handle long text gracefully
Accessibility:
Provide clear visual feedback
Support keyboard navigation
Use semantic markup
Consider screen reader users
Implementation:
Group related form elements
Handle disabled states
Consider form validation
Maintain consistent spacing
Input
Checkbox
RadioGroup
Select
Switch
Form
Field