Orbit Toggle Switch
A dynamic toggle switch where both the track and thumb rotate smoothly when switching states, creating a visually engaging interaction.
1. Copy the component file
Create a new file called orbit-toggle-switch.jsx in
your reusable components folder (for example
/src/components/) and paste the following code
into it.
orbit-toggle-switch.jsx
import { memo, useState } from "react";
import styles from "./orbit-toggle-switch.module.css";
const OrbitToggleSwitch = (props) => {
const {
id,
checked,
defaultChecked = false,
disabled = false,
onChange,
isInsideLabel,
inputProps,
} = props;
const isControlled = checked !== undefined;
const [switchChecked, setSwitchChecked] = useState(defaultChecked);
const isChecked = isControlled ? checked : switchChecked;
const toggle = () => {
if (disabled) return;
if (!isControlled) {
setSwitchChecked(!isChecked);
}
onChange?.(!isChecked);
};
const handleKeyDown = (e) => {
if ([" ", "Enter"].includes(e.key)) {
e.preventDefault();
toggle();
}
};
return (
<div
id={id}
role="checkbox"
tabIndex={disabled ? -1 : 0}
aria-checked={isChecked}
aria-disabled={disabled}
className={styles["orbit-toggle-switch"]}
onKeyDown={handleKeyDown}
{...(!isInsideLabel && ({
onClick: toggle,
}))}
>
{isInsideLabel && (
<input
type="checkbox"
checked={isChecked}
onChange={toggle}
aria-hidden={true}
hidden
{...inputProps}
/>
)}
<div className={styles["thumb"]}></div>
<div className={styles["track"]}></div>
</div>
);
};
export default memo(OrbitToggleSwitch); 2. Copy the CSS module file
In the same folder, create a file called orbit-toggle-switch.module.css and paste the following CSS.
orbit-toggle-switch.module.css
.orbit-toggle-switch {
--size: 16px;
--gap: 8px;
--track-color-unchecked: #bbb;
--track-color-checked: #5a6be5;
--thumb-color: white;
--transition-duration: 250ms;
@media (prefers-color-scheme: dark) {
--track-color-unchecked: #888;
--track-color-checked: #8d98e7;
--thumb-color: black;
}
position: relative;
display: inline-block;
cursor: pointer;
.track {
display: block;
width: calc(calc(var(--size) * 2) + calc(var(--gap) * 2));
height: calc(var(--size) + var(--gap));
background: var(--track-color-unchecked);
border-radius: calc(var(--size) * 2);
z-index: 2;
transition: all var(--transition-duration) ease-in-out;
}
.thumb {
position: absolute;
left: var(--gap);
top: 50%;
width: var(--size);
height: var(--size);
background: var(--thumb-color);
border-radius: 50%;
transform: translateY(-50%) rotate(0deg);
transform-origin: 100% 50%;
z-index: 3;
transition: all var(--transition-duration) ease-in-out;
}
&[aria-disabled="true"] {
cursor: initial;
pointer-events: none;
opacity: 0.65;
}
&[aria-checked="true"] {
.track {
background: var(--track-color-checked);
transform: rotate(180deg);
}
.thumb {
transform: translateY(-50%) rotate(-180deg);
}
}
} 3. Use the component
Now you can import and use the component anywhere in your project.
orbit-toggle-switch-preview.jsx
import { useCallback, useState } from "react";
import OrbitToggleSwitch from "@/components/essentials/orbit-toggle-switch/orbit-toggle-switch";
const labelStyles = {
display: "flex",
alignItems: "center",
gap: "8px",
};
const OrbitToggleSwitchPreview = () => {
const [checked, setChecked] = useState(false);
const handleChange = useCallback((checked) => {
setChecked(checked);
});
return (
<div>
<label style={labelStyles}>
<OrbitToggleSwitch
checked={checked}
onChange={handleChange}
isInsideLabel
/>
<span>
Did You Try Turning It {checked ? "Off" : "On"}?
</span>
</label>
</div>
);
}
export default OrbitToggleSwitchPreview; Disabled Switch
orbit-toggle-switch
import OrbitToggleSwitch from "@/components/essentials/orbit-toggle-switch/orbit-toggle-switch";
const OrbitToggleSwitchDisabledPreview = () => {
return (
<div>
<OrbitToggleSwitch
defaultChecked={false}
disabled
/>
</div>
);
}
export default OrbitToggleSwitchDisabledPreview; Props
Configure the component with the following props:
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| id | string | No | — | Assigned to the toggle switch control element. |
| checked | boolean | No | — | Controls the checked state (controlled mode). |
| defaultChecked | boolean | No | false | Sets the initial checked state (uncontrolled mode). |
| disabled | boolean | No | false | Disables the toggle and prevents interaction. |
| onChange | (checked: boolean) => void | No | — | Callback triggered when the toggle state changes. Receives the new true/false value. |
| isInsideLabel | boolean | No | false | Set to true when used inside a <label> to enable native label click accessibility behavior. |
| inputProps | React.InputHTMLAttributes<HTMLInputElement> | No | — | Additional props passed to the native input when isInsideLabel is true. |