Skip to content
fayeuiStatic / 0.1.0
LLMs
Components / Static edition

Checkboxes, radios and switches

Selection groups with explicit controlled state and native labels.

Quick start

After the application setup, copy this component into your application. It imports only public packages; the complete live demo later on this page also uses documentation layout helpers.

'use client';
import { useState } from 'react';
import { Radio } from '@fayegram-ai/faye-ui/controls/radio';
import { RadioGroup } from '@fayegram-ai/faye-ui/controls/radio-group';
import { Switch } from '@fayegram-ai/faye-ui/controls/switch';
import { ControlLabel } from '@fayegram-ai/faye-ui/controls/field-layout';
import { Stack } from '@fayegram-ai/faye-ui/layout/layout';

export function Preferences() {
  const [pace, setPace] = useState('slow');
  const [notifications, setNotifications] = useState(true);
  return <Stack gap="4">
    <RadioGroup name="pace" aria-label="Your pace" value={pace} onValueChange={value => setPace(String(value))}>
      <Stack gap="3">{['slow', 'adventurous'].map(value => <ControlLabel key={value} label={value}>
        {attributes => <Radio.Root {...attributes} value={value}><Radio.Indicator /></Radio.Root>}
      </ControlLabel>)}</Stack>
    </RadioGroup>
    <ControlLabel label="Enable notifications" description="Hear about new places.">
      {attributes => <Switch.Root {...attributes} checked={notifications} onCheckedChange={setNotifications}>
        <Switch.Thumb />
      </Switch.Root>}
    </ControlLabel>
  </Stack>;
}

API reference: Radio, Radio Group, Switch, Field Layout.

Usage contract

Checkboxes model independent selections. Radio groups model exclusive choices. Switches represent immediate on/off settings. Disabled, indeterminate and read-only are separate states. The examples supply labels instead of relying on position or color.

Read-only checkboxes, radios and switches retain their value and keyboard focus indicator without advertising a change through hover or press effects. Their controls and ControlLabel labels use a neutral cursor. Set readOnly on a radio group or an individual choice; use disabled when the control must also leave keyboard interaction. Disabled appearance takes precedence when both states apply.

Customization

Use semantic tokens for visual changes, public component parts for structural customization, and explicit props for instance behavior. The surrounding site lets you compare light and dark, different brands and density.

Source and verification

The examples below import actual public package entries. Full source is downloadable. Source coverage does not establish keyboard, hydration or browser compatibility; those require the application test suite.

Live examples

Try the controls below, then read or copy the complete example to adapt it for your application.

Checkbox groups

Channels: email

Mutually exclusive options

Frequency: weekly

Immediate state changes

Notifications enabled
Read the complete example
'use client';
import { Check, Minus } from '@lucide/icons';
import { Icon } from '@fayegram-ai/faye-ui/core/icon';
import { useState } from 'react';
import { Checkbox } from '@fayegram-ai/faye-ui/controls/checkbox';
import { CheckboxGroup } from '@fayegram-ai/faye-ui/controls/checkbox-group';
import { Radio } from '@fayegram-ai/faye-ui/controls/radio';
import { RadioGroup } from '@fayegram-ai/faye-ui/controls/radio-group';
import { Switch } from '@fayegram-ai/faye-ui/controls/switch';
import { ControlLabel } from '@fayegram-ai/faye-ui/controls/field-layout';
import { ThemeScope } from '@fayegram-ai/faye-ui/provider/provider';
import { Stack, Inline } from '@fayegram-ai/faye-ui/layout/layout';
import { Section, Result } from '../example';
export default function Selection() {
    const [channels, setChannels] = useState<string[]>(['email']), [frequency, setFrequency] = useState('weekly'), [enabled, setEnabled] = useState(true);
    return <><Section title="Checkbox groups"><CheckboxGroup value={channels} onValueChange={setChannels} aria-label="Notification channels"><Stack gap="3">{['email', 'inbox', 'mobile'].map(value => <ControlLabel key={value} label={value}>{attrs => <Checkbox.Root {...attrs} value={value}><Checkbox.Indicator><Icon icon={Check} size="xs"/></Checkbox.Indicator></Checkbox.Root>}</ControlLabel>)}</Stack></CheckboxGroup><Inline wrap gap="5"><ControlLabel label="Mixed state">{attrs => <Checkbox.Root {...attrs} indeterminate><Checkbox.Indicator><Icon icon={Minus} size="xs"/></Checkbox.Indicator></Checkbox.Root>}</ControlLabel><ControlLabel label="Disabled" disabled>{attrs => <Checkbox.Root {...attrs} defaultChecked><Checkbox.Indicator><Icon icon={Check} size="xs"/></Checkbox.Indicator></Checkbox.Root>}</ControlLabel></Inline><Result>Channels: {channels.join(', ') || 'none'}</Result></Section>
    <Section title="Mutually exclusive options"><RadioGroup value={frequency} onValueChange={value => setFrequency(String(value))} aria-label="Digest frequency"><Stack gap="3">{['daily', 'weekly', 'never'].map(value => <ControlLabel key={value} label={value} description={value === 'weekly' ? 'A weekly selection of places and stories, picked for you.' : undefined}>{attrs => <Radio.Root {...attrs} value={value}><Radio.Indicator/></Radio.Root>}</ControlLabel>)}</Stack></RadioGroup><Result>Frequency: {frequency}</Result></Section>
    <Section title="Immediate state changes"><ControlLabel label="Enable notifications" description="Hear about new places without losing your current view.">{attrs => <Switch.Root {...attrs} checked={enabled} onCheckedChange={setEnabled}><Switch.Thumb/></Switch.Root>}</ControlLabel><Result>Notifications {enabled ? 'enabled' : 'disabled'}</Result><ThemeScope direction="ltr"><ControlLabel label="Nested left-to-right switch">{attrs => <Switch.Root {...attrs} defaultChecked variants={{size: 'sm'}}><Switch.Thumb/></Switch.Root>}</ControlLabel></ThemeScope></Section></>;
}