or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-radix-ui--react-switch

A two-state control used to toggle between checked and unchecked states

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@radix-ui/react-switch@1.2.x

To install, run

npx @tessl/cli install tessl/npm-radix-ui--react-switch@1.2.0

index.mddocs/

Radix UI React Switch

@radix-ui/react-switch is a TypeScript React component library that provides an accessible, unstyled switch component. It implements a two-state control for toggling between checked and unchecked states with proper ARIA attributes, keyboard navigation, and form integration.

Package Information

  • Package Name: @radix-ui/react-switch
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install @radix-ui/react-switch

Core Imports

import * as Switch from "@radix-ui/react-switch";

Or with named imports:

import { Switch, SwitchThumb, Root, Thumb } from "@radix-ui/react-switch";

For CommonJS:

const Switch = require("@radix-ui/react-switch");

Basic Usage

import * as Switch from "@radix-ui/react-switch";
import { useState } from "react";

function SwitchDemo() {
  const [checked, setChecked] = useState(false);

  return (
    <form>
      <div style={{ display: "flex", alignItems: "center" }}>
        <label
          className="Label"
          htmlFor="airplane-mode"
          style={{ paddingRight: 15 }}
        >
          Airplane mode
        </label>
        <Switch.Root
          className="SwitchRoot"
          id="airplane-mode"
          checked={checked}
          onCheckedChange={setChecked}
        >
          <Switch.Thumb className="SwitchThumb" />
        </Switch.Root>
      </div>
    </form>
  );
}

Architecture

The component follows Radix UI's compound component pattern with two main parts:

  • Switch/Root: The main interactive button element that handles state and accessibility
  • SwitchThumb/Thumb: The visual indicator that shows the current state

The component supports both controlled and uncontrolled usage patterns, integrates seamlessly with HTML forms, and provides proper ARIA attributes for screen readers.

Capabilities

Switch Component

The main switch component that renders as an accessible button with switch role.

/**
 * Base primitive components from @radix-ui/react-primitive
 */
type PrimitivePropsWithRef<E extends React.ElementType> = React.ComponentPropsWithRef<E> & {
  asChild?: boolean;
};

interface PrimitiveForwardRefComponent<E extends React.ElementType>
  extends React.ForwardRefExoticComponent<PrimitivePropsWithRef<E>> {}

declare const Primitive: {
  button: PrimitiveForwardRefComponent<"button">;
  span: PrimitiveForwardRefComponent<"span">;
  input: PrimitiveForwardRefComponent<"input">;
};

/**
 * Element type for Switch component
 */
type SwitchElement = React.ComponentRef<typeof Primitive.button>;

/**
 * Props type for Primitive.button
 */
type PrimitiveButtonProps = React.ComponentPropsWithoutRef<typeof Primitive.button>;

/**
 * Main switch component that renders as a button with switch role
 */
const Switch: React.ForwardRefExoticComponent<
  SwitchProps & React.RefAttributes<SwitchElement>
>;

/**
 * Alternative export name for the Switch component
 */
const Root: React.ForwardRefExoticComponent<
  SwitchProps & React.RefAttributes<SwitchElement>
>;

interface SwitchProps extends PrimitiveButtonProps {
  /** The controlled checked state of the switch */
  checked?: boolean;
  /** The checked state of the switch when it is initially rendered. Use when you do not need to control its checked state */
  defaultChecked?: boolean;
  /** When true, indicates that the user must check the switch before the owning form can be submitted */
  required?: boolean;
  /** The name of the switch. Submitted with its owning form as part of a name/value pair */
  name?: string;
  /** The value given as data when submitted with a `name`. Defaults to "on" */
  value?: string;
  /** Associates the control with a form element */
  form?: string;
  /** When true, prevents the user from interacting with the switch */
  disabled?: boolean;
  /** Event handler called when the checked state of the switch changes */
  onCheckedChange?(checked: boolean): void;
}

Key Features:

  • Supports both controlled (checked + onCheckedChange) and uncontrolled (defaultChecked) patterns
  • Automatically integrates with HTML forms through hidden input element with name and value props
  • Provides proper ARIA attributes (role="switch", aria-checked, aria-required)
  • Handles keyboard navigation (Space and Enter keys toggle state)
  • Includes form integration with proper event bubbling and validation support
  • Uses internal SwitchBubbleInput component for seamless form submission

Data Attributes:

  • data-state: "checked" | "unchecked" - Current switch state
  • data-disabled: Present when disabled

Switch Thumb Component

The visual indicator component that displays the current state of the switch.

/**
 * Element type for SwitchThumb component
 */
type SwitchThumbElement = React.ComponentRef<typeof Primitive.span>;

/**
 * Props type for Primitive.span
 */
type PrimitiveSpanProps = React.ComponentPropsWithoutRef<typeof Primitive.span>;

/**
 * Visual thumb/handle component that indicates the switch state
 */
const SwitchThumb: React.ForwardRefExoticComponent<
  SwitchThumbProps & React.RefAttributes<SwitchThumbElement>
>;

/**
 * Alternative export name for the SwitchThumb component
 */
const Thumb: React.ForwardRefExoticComponent<
  SwitchThumbProps & React.RefAttributes<SwitchThumbElement>
>;

interface SwitchThumbProps extends PrimitiveSpanProps {}

Key Features:

  • Automatically receives state from parent Switch component through context
  • Renders as a span element for styling flexibility
  • Inherits data attributes from Switch context for consistent styling

Data Attributes:

  • data-state: "checked" | "unchecked" - Current switch state
  • data-disabled: Present when disabled

Context Utilities

Utility function for creating component scopes when composing multiple switch instances.

/**
 * Scope type for context management
 */
type Scope<C = any> = { [scopeName: string]: React.Context<C>[] } | undefined;

/**
 * Hook type returned by createSwitchScope
 */
type ScopeHook = (scope: Scope) => { [__scopeSwitch: string]: Scope };

/**
 * Creates a scope for switch components to avoid context conflicts
 * Used when composing multiple switch instances or building compound components
 */
function createSwitchScope(): ScopeHook;

Usage:

import { createSwitchScope } from "@radix-ui/react-switch";

// Create a scoped context for this specific switch instance
const useScope = createSwitchScope();

function MyComponent() {
  const scope = useScope();
  
  return (
    <Switch.Root {...scope}>
      <Switch.Thumb />
    </Switch.Root>
  );
}

Usage Examples

Controlled Switch

import * as Switch from "@radix-ui/react-switch";
import { useState } from "react";

function ControlledSwitch() {
  const [isChecked, setIsChecked] = useState(false);

  return (
    <Switch.Root checked={isChecked} onCheckedChange={setIsChecked}>
      <Switch.Thumb />
    </Switch.Root>
  );
}

Uncontrolled Switch

import * as Switch from "@radix-ui/react-switch";

function UncontrolledSwitch() {
  return (
    <Switch.Root defaultChecked={true}>
      <Switch.Thumb />
    </Switch.Root>
  );
}

Form Integration

import * as Switch from "@radix-ui/react-switch";

function FormSwitch() {
  return (
    <form onSubmit={(e) => {
      e.preventDefault();
      const formData = new FormData(e.currentTarget);
      console.log(formData.get("notifications")); // "on" or null
    }}>
      <div style={{ display: "flex", alignItems: "center" }}>
        <label htmlFor="notifications-switch" style={{ paddingRight: 15 }}>
          Enable notifications
        </label>
        <Switch.Root 
          id="notifications-switch"
          name="notifications" 
          value="on"
          defaultChecked={false}
        >
          <Switch.Thumb />
        </Switch.Root>
      </div>
      <button type="submit">Submit</button>
    </form>
  );
}

Form Integration Details:

  • The name prop determines the form field name in the submitted data
  • The value prop sets the value when checked (defaults to "on")
  • When unchecked, the field is not included in form submission (standard HTML behavior)
  • A hidden input element is automatically created to handle form submission

Disabled Switch

import * as Switch from "@radix-ui/react-switch";

function DisabledSwitch() {
  return (
    <div style={{ display: "flex", alignItems: "center" }}>
      <label htmlFor="disabled-switch" style={{ paddingRight: 15 }}>
        Cannot change this
      </label>
      <Switch.Root id="disabled-switch" disabled defaultChecked={true}>
        <Switch.Thumb />
      </Switch.Root>
    </div>
  );
}

With Custom Styling

import * as Switch from "@radix-ui/react-switch";
import "./switch.css"; // Your custom styles

function StyledSwitch() {
  return (
    <Switch.Root className="switch-root">
      <Switch.Thumb className="switch-thumb" />
    </Switch.Root>
  );
}

Example CSS:

.switch-root {
  width: 42px;
  height: 25px;
  background-color: gray;
  border-radius: 9999px;
  position: relative;
  border: none;
  cursor: pointer;
}

.switch-root[data-state="checked"] {
  background-color: blue;
}

.switch-root[data-disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

.switch-thumb {
  display: block;
  width: 21px;
  height: 21px;
  background-color: white;
  border-radius: 9999px;
  transition: transform 100ms;
  transform: translateX(2px);
  will-change: transform;
}

.switch-thumb[data-state="checked"] {
  transform: translateX(19px);
}

.switch-thumb[data-disabled] {
  opacity: 0.8;
}

Accessibility Features

  • ARIA Compliant: Uses proper role="switch", aria-checked, and aria-required attributes
  • Keyboard Navigation: Space and Enter keys toggle the switch state
  • Screen Reader Support: State changes are announced to assistive technologies
  • Focus Management: Proper focus indicators and keyboard navigation
  • Form Integration: Works seamlessly with native HTML form validation and submission
  • Disabled State: Properly handles disabled state with appropriate ARIA attributes and visual indicators