Styled switch toggle with label and description, built on the @foldkit/ui Switch helper.
Receive emails about your account activity.
Add an extra layer of security to your account.
Add this component to your project:
npx shadcn@latest add @foldcn/switchThe component ships as plain source โ no build step, no wrapper. Copy it and make it yours.
registry/default/ui/switch.ts94 linesimport { Switch as FoldkitSwitch } from '@foldkit/ui'
import type { Html, HtmlBuilder } from 'foldkit/html'
import { cn } from '@/lib/utils'
export const switchClass =
'peer group/switch inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input dark:data-[state=unchecked]:bg-input/80'
export const switchThumbClass =
'pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground dark:data-[state=unchecked]:bg-foreground'
const switchThumbOn = 'translate-x-4'
const switchThumbOff = 'translate-x-0'
export const switchLabelClass =
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
export const switchDescriptionClass = 'text-sm text-muted-foreground'
export const switchWrapperClass = 'flex items-center gap-3'
export const switchTextWrapperClass = 'flex flex-col gap-1'
export type SwitchConfig<M> = Readonly<{
id: string
isChecked: boolean
onToggle: (isChecked: boolean) => M
label: string
maybeDescription?: string
isDisabled?: boolean
isReadOnly?: boolean
name?: string
value?: string
className?: string
thumbClass?: string
labelClass?: string
descriptionClass?: string
wrapperClass?: string
}>
/** Styled switch with label and optional description, built on the
* @foldkit/ui Switch helper. */
export const switch_ = <M>(config: SwitchConfig<M>, h: HtmlBuilder<M>): Html =>
FoldkitSwitch.view<M>(
{
id: config.id,
isChecked: config.isChecked,
onToggle: config.onToggle,
isDisabled: config.isDisabled,
isReadOnly: config.isReadOnly,
name: config.name,
value: config.value,
toView: (attributes) =>
h.div(
[h.Class(cn(switchWrapperClass, config.wrapperClass))],
[
h.button(
[...attributes.button, h.Class(cn(switchClass, config.className))],
[
h.span([
h.Class(
cn(
switchThumbClass,
config.isChecked ? switchThumbOn : switchThumbOff,
config.thumbClass,
),
),
]),
],
),
h.div(
[h.Class(switchTextWrapperClass)],
[
h.label(
[...attributes.label, h.Class(cn(switchLabelClass, config.labelClass))],
[config.label],
),
config.maybeDescription === undefined
? h.empty
: h.p(
[
...attributes.description,
h.Class(cn(switchDescriptionClass, config.descriptionClass)),
],
[config.maybeDescription],
),
],
),
],
),
},
h,
)