API
Two components, one environment flag and two commands. Both components are safe in React Server Components; you don’t need "use client" in your own files.
import { Switches, Variant } from "switches-react";<Switches />
The panel. It takes no props. Render it once in the whole app, as the first child of <body> (see Getting started for each framework). It outputs a small inline script, which applies saved and linked picks before first paint, and the panel itself.
<Variant>
Marks one version of a section.
| Prop | Type | What it does |
|---|---|---|
name | string, required | The section. Every version of one section has the same name. The panel shows it in plain words. |
option | string, required | This version’s name, unique within the section. Picks, links and copied text refer to it. |
label | string | Text for this version in the panel. Without it, the option is shown in plain words. |
swatch | string | A colour (#2563eb, rgb(…), hsl(…) or a name). The panel draws a dot of it beside the label. For colour choices. |
icon | “sun”, “moon” or “system” | An icon drawn beside the label in the panel. For light, dark and system choices. |
default | boolean | Show this version when nothing is picked. Without it, the first version is the default. |
children | ReactNode | The version itself. |
<Variant name="pricing" option="tiers" default>
<PricingTiers />
</Variant>
<Variant name="pricing" option="simple" label="One plan">
<PricingSimple />
</Variant>Rule: versions sit next to each other
All versions of one section must be siblings: under the same parent element, one after another, with nothing in between. Every version is in the HTML, and Switches hides the ones not showing with CSS sibling selectors. Versions split across different parents all show at once.
Rule: unique names
A section name must be unique across the whole site, because picks are site-wide. Option names must be unique within their section; a duplicate is ignored with a console warning. Name versions after the idea (phone, split), not a or b: clients see them, and letters stop meaning anything once versions are added or removed.
Which version shows by default
With no pick, the version marked default shows; without one, the first version. A pick for a version that no longer exists is ignored, and the default shows. Picks for sections that no longer exist are forgotten after the next visit. Mark one default per section.
What a Variant adds to the page
Each Variant renders a <div style="display: contents"> around its children, plus a small <style>. Layout is unaffected, but CSS that relies on direct children (>) or on :first-child / :nth-child may need adjusting while the versions are in place.
NEXT_PUBLIC_SWITCHES=off
An emergency switch. Set it as an environment variable and rebuild (Next.js reads NEXT_PUBLIC_ values at build time; other bundlers need to replace process.env.NEXT_PUBLIC_SWITCHES themselves, for example with Vite’s define, or the flag has no effect). Then:
<Switches />renders nothing: no panel, no script;- every section shows its default version, whatever is in the link or the browser;
- the other versions are still in the HTML, only hidden. The code, the wrappers and the package all stay.
It does not clean anything up. To launch, finish Switches instead (see Claude Code).
Production build warning
While <Switches /> renders in a production build and the flag isn’t off, the server or build output prints this, once per process. In a client-only app (such as Vite) it appears in the browser console instead; if the bundler does not define NODE_ENV, nothing is printed.
[switches] Switches is still installed in this production build. Remove it before launch: ask Claude to "finish" Switches.This is a reminder, not an error: Switches is meant to run on deployed previews. With NEXT_PUBLIC_SWITCHES=off it is not printed.
Commands
npx switches init [--mode off|ask|always]
npx switches uninitinitwrites the Claude Code skill to.claude/skills/switches/SKILL.mdand adds or updates the Switches block inCLAUDE.md(creating the file if needed). The mode defaults toask. Running it again replaces the block and the skill, so it is also how you change the mode.uninitremoves the skill folder and the Switches block. If the block was all thatCLAUDE.mdheld, the file is deleted.
The block in CLAUDE.md sits between two markers. Only the text between them is ever rewritten, so the rest of the file is safe. If there is more than one start or end marker, the commands stop and ask you to fix it by hand.
<!-- switches:start -->
## Switches
Switches version-first mode: ask
…
<!-- switches:end -->