Switch

On/off control for settings, preferences, and feature states.

Installation

npx shadcn@latest add @iconiq/r-switch

File Structure

Usage

"use client";

import { useState } from "react";
import { Switch } from "@/components/ui/r-switch";

export function SwitchPreview() {
  const [enabled, setEnabled] = useState(true);

  return (
    <div className="flex min-h-[14rem] w-full max-w-md items-center justify-center px-4 py-6">
      <Switch
        checked={enabled}
        id="motion-enabled"
        onCheckedChange={setEnabled}
        label="Enable motion"
        description="Turn animated transitions on or off for this workspace."
      />
    </div>
  );
}

Props

Props
Description

Switch

checked

Controlled checked state. Pass this when the parent owns the current on or off value.

Type boolean

defaultChecked

Initial checked state for uncontrolled usage. The component keeps its local animation state in sync with this mode too.

Type boolean

onCheckedChange

Called whenever the switch changes state, after the thumb and fill animation sequence starts.

Type (checked: boolean) => void

label

Optional label rendered beside the switch. When provided with description, both sit inside a native label element linked by htmlFor.

Type React.ReactNode

description

Optional helper copy rendered under the label and linked through aria-describedby.

Type React.ReactNode

labelSide

Controls which side of the switch the label and description block appears on.

Type "left" | "right"·Default right

labelClassName

Merged onto the label text wrapper.

Type string

descriptionClassName

Merged onto the description text wrapper.

Type string

size

Scales the track, thumb, label text, and row gap together.

Type "sm" | "default" | "lg"·Default default

disabled

Disables pointer and keyboard interaction, and dims the switch and optional label together.

Type boolean

readOnly

Shows the current state without allowing toggles. Useful for locked preference rows.

Type boolean·Default false

required

Forwards native required validation to the hidden input and appends a visual asterisk to the label.

Type boolean·Default false

invalid

Applies destructive ring styling and aria-invalid for form validation feedback.

Type boolean·Default false

className

Merged onto the switch track for local spacing or surface overrides.

Type string

wrapperClassName

Merged onto the outer label wrapper when label or description text is present.

Type string

Motion and interaction behavior

The switch uses separate motion values for thumb travel, thumb squash, and track fill opacity so the state change feels tactile without becoming noisy.

Built on Radix switch root and thumb primitives while preserving the same motion values for thumb travel, thumb squash, and track fill opacity as the core switch.

Pointer press slightly flattens the thumb before release, then the thumb snaps back with a softer bounce after the state change.

The dark foreground fill fades in as the thumb travels right, rather than swapping track color instantly.

Controlled and uncontrolled usage both keep the thumb animation synchronized with the underlying switch state.

Thumb travel, squash, and fill animations honor prefers-reduced-motion automatically.

The track keeps a fixed left-to-right thumb direction via dir=ltr so RTL layouts do not invert the on state.

Registry bundle

Install the exact registry entry shown on the right when you want the component file and its declared runtime dependencies together.

Dependencies: @radix-ui/react-switch, motion.

Installs a Radix switch with the same label, description, size, validation, and readOnly API as the Base UI version.

Uses Radix switch primitives under the exact same Iconiq track, thumb, and press-motion shell as the core switch component.

The generated registry file is /r/r-switch.json.

Contact

Additionally, if you find any bug or issue, feel free to raise an issue.