Switch für React
An und aus für Einstellungen, die sofort gelten, mit role="switch", als Liste oder als Zeilen.
Eine von 9 Components in Forms und Inputs
Ein und aus, sofort
Ein Switch schaltet eine Einstellung mit sofortiger Wirkung ein oder aus, wie ein Lichtschalter. Dieser ist ein echter Button mit role switch und aria-checked, Screenreader sagen ihn als ein oder aus an, die Leertaste schaltet, und das Label gehört zur Klickfläche.
Die Einstellungsliste stapelt Switches mit je einem Titel und einem Satz, das Layout einer Benachrichtigungsseite. „Zeilen mit Icons“ ergänzt ein Icon je Zeile und passt zu App-Einstellungen wie Dark Mode, Töne und Automatisch speichern. Beide zeigen den Zustand mit Position und Farbe, der Knopf bewegt sich ruhig, bei reduzierter Bewegung gar nicht.
Nutze einen Switch nur, wenn die Änderung sofort gilt. In einem Formular, das mit einem Button gespeichert wird, nimm eine Checkbox. Benenne die Einstellung, nicht den Zustand, E-Mail-Benachrichtigungen statt E-Mail einschalten, der Switch zeigt selbst, ob er an ist.
Details
Was Schalter mitbringt, aus Registry und Quellcode gelesen.
- Kategorie
- Forms und Inputs
- Bereich
- Elements
- Variants
- 2
- Version
- 1.0.0 · seit 1.0.0
- Datei
- components/ui/switch.tsx
- Bringt mit
- utilskiticon
- npm-Pakete
- Keine
- Sprachen
- Texte kommen über Props
- Studio
- Im Code anpassen
- Mit KI bauen
- Per MCP in Claude, Cursor, Copilot und Codex
Props
Die Schnittstelle SwitchProps aus components/ui/switch.tsx. Beschreibungen stammen aus den Kommentaren im Code und sind auf Englisch. Nimmt außerdem alle Props von Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onChange' | 'value'> an.
checkedoptionalbooleandefaultCheckedoptionalbooleanonCheckedChangeoptional(checked: boolean) => voidlabeloptionalReact.ReactNodedescriptionoptionalReact.ReactNodesizeoptional'sm' | 'md'positionoptional'start' | 'end'Where the switch sits next to the label. end is the settings-list look.
variantoptional'default' | 'row'row: a bordered settings row, optionally with an icon
iconoptionalIconNamenameoptionalstringForm name, a hidden input carries "on" when checked
Barrierefreiheit
Aus dem Quellcode der Component abgeleitet.
- Setzt aria-checked, aria-describedby und aria-labelledby, damit Screenreader Zweck und Zustand ansagen.
- Nutzt die Rolle switch.
- Jedes Feld hat ein Label, sichtbar oder für Screenreader.
- Zeigt bei Bedienung mit der Tastatur einen sichtbaren Fokusring.
- Der Server rendert den Startzustand vollständig, JavaScript ergänzt nur die Interaktion.
Bewegung
- Bei reduzierter Bewegung (prefers-reduced-motion) bleibt sie ruhig. 2 Stellen im Code schalten Bewegung dann ab.
- Ohne Animationen, sie steht sofort ruhig da.
Quellcode
Die Datei, die aiw add in dein Projekt legt. Sie gehört danach dir.
Datei anzeigen, 138 Zeilen
'use client';
import { useId, useState } from 'react';
import { cn } from '@/lib/kit';
import { Icon, type IconName } from '@/components/ui/icon';
export interface SwitchProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onChange' | 'value'> {
checked?: boolean;
defaultChecked?: boolean;
onCheckedChange?: (checked: boolean) => void;
label?: React.ReactNode;
description?: React.ReactNode;
size?: 'sm' | 'md';
/** Where the switch sits next to the label. end is the settings-list look. */
position?: 'start' | 'end';
/** row: a bordered settings row, optionally with an icon */
variant?: 'default' | 'row';
icon?: IconName;
/** Form name, a hidden input carries "on" when checked */
name?: string;
className?: string;
}
const SIZE = {
sm: { track: 'h-5 w-9', thumb: 'size-4', on: 'translate-x-4', mark: 8 },
md: { track: 'h-6 w-11', thumb: 'size-5', on: 'translate-x-5', mark: 10 },
};
/**
* An on/off setting that applies right away. A button with role="switch" and aria-checked.
* <Switch label="Email digest" description="A summary every Monday" defaultChecked />
*/
export function Switch({ checked, defaultChecked, onCheckedChange, label, description, size = 'md', position = 'end', variant = 'default', icon, name, className, id, disabled, onClick, ...rest }: SwitchProps) {
const auto = useId();
const sid = id ?? auto;
const [inner, setInner] = useState(defaultChecked ?? false);
const on = checked ?? inner;
const s = SIZE[size];
const control = (
<button
type="button"
role="switch"
id={sid}
aria-checked={on}
aria-labelledby={label ? `${sid}-label` : undefined}
aria-describedby={description ? `${sid}-desc` : undefined}
disabled={disabled}
{...rest}
onClick={(e) => {
onClick?.(e);
setInner(!on);
onCheckedChange?.(!on);
}}
className={cn(
'group/switch relative inline-flex shrink-0 cursor-pointer items-center rounded-full p-0.5 outline-none transition-[background-color,box-shadow] duration-200 ease-[var(--ease-kit)]',
'bg-[color-mix(in_oklab,var(--foreground)_14%,var(--background))] shadow-[inset_0_0_0_1px_color-mix(in_oklab,var(--foreground)_6%,transparent)] hover:bg-[color-mix(in_oklab,var(--foreground)_20%,var(--background))]',
'aria-checked:bg-primary aria-checked:hover:bg-[color-mix(in_oklab,var(--primary)_88%,var(--background))]',
'focus-visible:shadow-[0_0_0_3px_color-mix(in_oklab,var(--primary)_25%,transparent)]',
'disabled:cursor-not-allowed disabled:opacity-50',
// 44 px hit area
'after:absolute after:-inset-x-1 after:-inset-y-3 after:content-[""]',
s.track,
!label && className,
)}
>
{/* The knob travels on a spring: it overshoots a hair and settles. Held down it stretches. A small check draws itself on it. */}
<span
aria-hidden
className={cn(
'pointer-events-none grid place-items-center rounded-full shadow-[0_1px_3px_rgb(0_0_0/0.2),0_0_0_0.5px_rgb(0_0_0/0.04)] transition-[translate,scale,background-color] duration-[420ms] ease-[cubic-bezier(0.34,1.45,0.64,1)] group-active/switch:scale-x-[1.18] group-active/switch:duration-150 motion-reduce:transition-none',
s.thumb,
on ? cn(s.on, 'origin-right bg-primary-foreground') : 'origin-left bg-background dark:bg-[color-mix(in_oklab,var(--foreground)_88%,var(--background))]',
)}
>
<svg viewBox="0 0 24 24" width={s.mark} height={s.mark} fill="none" className="text-primary">
<path
d="m5 12.5 4.5 4.5L19 7.5"
pathLength={1}
strokeDasharray="1 1"
stroke="currentColor"
strokeWidth="3.4"
strokeLinecap="round"
strokeLinejoin="round"
className={cn('transition-[stroke-dashoffset,opacity] ease-[var(--ease-kit)] motion-reduce:transition-none', on ? 'opacity-100 delay-150 duration-300 [stroke-dashoffset:0]' : 'opacity-0 duration-150 [stroke-dashoffset:1]')}
/>
</svg>
</span>
</button>
);
const hidden = name ? <input type="hidden" name={name} value={on ? 'on' : ''} /> : null;
if (!label) return (
<>
{control}
{hidden}
</>
);
const text = (
<span className="min-w-0 flex-1">
<label id={`${sid}-label`} htmlFor={sid} className={cn('block cursor-pointer text-sm font-medium leading-5 text-foreground', disabled && 'cursor-not-allowed')}>
{label}
</label>
{description && (
<span id={`${sid}-desc`} className="mt-0.5 block text-[13px] leading-5 text-muted-foreground">
{description}
</span>
)}
</span>
);
return (
<div
className={cn(
'flex items-start gap-3.5',
variant === 'row' && 'items-center rounded-xl border border-border bg-card px-4 py-3.5 shadow-[var(--shadow-sm)]',
disabled && 'opacity-60',
className,
)}
>
{variant === 'row' && icon && (
<span
className={cn(
'grid size-9 shrink-0 place-items-center rounded-lg border bg-surface transition-[color,border-color,background-color] duration-300',
on ? 'border-[color-mix(in_oklab,var(--primary)_35%,var(--border))] bg-[color-mix(in_oklab,var(--primary)_8%,var(--surface))] text-[color-mix(in_oklab,var(--primary)_85%,var(--foreground))]' : 'border-border text-muted-foreground',
)}
>
<Icon name={icon} size={18} />
</span>
)}
{position === 'start' && <span className={cn('flex items-center', size === 'md' ? 'h-6 -mt-0.5' : 'h-5')}>{control}</span>}
{text}
{position === 'end' && <span className={cn('flex items-center', variant === 'row' ? '' : size === 'md' ? 'h-6 -mt-0.5' : 'h-5')}>{control}</span>}
{hidden}
</div>
);
}Häufige Fragen
Wie installiere ich Schalter?
Mit aiw add @ai-website/switch. Der Befehl legt die Datei components/ui/switch.tsx in dein Projekt, der Code gehört danach dir. Mit dabei sind icon. Weitere npm-Pakete braucht sie nicht.
Welche Varianten hat Schalter?
2 Varianten, Einstellungsliste und Zeilen mit Icons.
Kann ein KI-Assistent Schalter einbauen?
Ja. Über den MCP-Server der aiw CLI finden Claude, Cursor, GitHub Copilot, Windsurf, Codex und Gemini Schalter mit aiw_components und installieren sie mit aiw_add @ai-website/switch. Nenn im Prompt die Variante, etwa variant="row", dann schreibt der Assistent deine Inhalte direkt in die Props.