
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
variations
Advanced tools
A React component library that enables rapid prototyping and exploration of UI variations.

npm install variations
# or
yarn add variations
# or
pnpm add variations
Wrap your application with the VariationsProvider at the root level. The VariationControls component provides a UI for switching between different variations in your app. You can style it or position it as needed.:
"use client";
import { VariationsProvider, VariationsControls } from "variations";
function RootLayout({ children }: { children: React.ReactNode }) {
return (
<VariationsProvider>
{children}
<VariationsControls position="bottom-center" />
</VariationsProvider>
);
}
By default, variations are saved to and loaded from the URL query string so that you can share variations with others. You can disable this behavior by setting disableQueryString to true:
<VariationsProvider disableQueryString={true}>
{children}
<VariationsControls position="bottom-center" />
</VariationsProvider>
The URL query string uses a clean, readable format:
http://localhost:3000/?var=group.id_group.id_group.id&s=base64_encoded_state
For example:
http://localhost:3000/?var=root.login-page_login-form.option-2_email-form.show&s=eyJ0aGVtZSI6eyJwcmltYXJ5Q29sb3IiOiIjMDAwIn19
Where:
. separates a group from its ID (e.g., login-form.option-2)_ separates different variations (e.g., login-form.option-2_email-form.show)s contains the base64-encoded JSON of your global stateThis format makes it easy to share not just variations but also the entire application state with others. The state is automatically synchronized with the URL, so you can:
You can disable this behavior by setting disableQueryString to true:
<VariationsProvider disableQueryString={true}>{children}</VariationsProvider>
Note for Next.js users: Variations requires the "use client" directive to be present in the root component.
"use client";
import { Variations, Variation } from "variations";
export default function MyComponent() {
return (
<Variations label="Button Styles">
<Variation label="Primary">
<button className="bg-blue-500 text-white">Click me</button>
</Variation>
<Variation label="Secondary">
<button className="bg-gray-500 text-white">Click me</button>
</Variation>
</Variations>
);
}
You can create nested variations to explore combinations of different components:
<Variations label="Layout">
<Variation label="Sidebar">
<div className="flex">
<Variations label="Theme">
<Variation label="Light">
<div className="bg-white">{/* Content */}</div>
</Variation>
<Variation label="Dark">
<div className="bg-gray-900">{/* Content */}</div>
</Variation>
</Variations>
</div>
</Variation>
<Variation label="Top Nav">
<div className="flex flex-col">{/* Similar nested variations */}</div>
</Variation>
</Variations>
The root component that manages a set of variations.
Props:
children: React nodes containing Variation componentsdisableQueryString (boolean, optional): When true, disables the URL query string functionality. This prevents variations from being saved to or loaded from the URL. Defaults to falseThe root component that manages a set of variations.
Props:
label (string): The label for this set of variationsisRoot (boolean, optional): When true, indicates this is the root variations component. Only one root component should exist in the tree. Defaults to falsechildren: React nodes containing Variation componentsThe component that wraps each variation.
Props:
label (string): The display name for this variationchildren: React nodes to render when this variation is activeA pre-built UI component that provides a control panel for managing variations.
Props:
position (string, optional): The position of the control panel. Possible values are bottom-center, bottom-left, bottom-right, middle-left, middle-right, top-center, top-left, top-right. Defaults to bottom-centerThe Controls component automatically displays all registered variations and allows users to switch between them. It updates in real-time as variations are added or removed from your app. You can show and hide the controls by clicking on the Variations icon or by using the keyboard shortcut Option + v. The controls support keyboard navigation or clicking on the controls to switch between variations.
A hook for accessing and updating global state that lives alongside your variations. This state is independent of any specific variation and can be used to store any application-wide data. The hook follows React's useState pattern, returning a tuple of the state and a setter function.
interface GlobalState {
theme: {
primaryColor: string;
fontSize: number;
};
preferences: {
showAdvancedOptions: boolean;
};
}
// First, provide initial state through the provider
function App() {
const initialState: GlobalState = {
theme: {
primaryColor: "#000000",
fontSize: 16,
},
preferences: {
showAdvancedOptions: false,
},
};
return (
<VariationsProvider initialState={initialState}>
<YourApp />
</VariationsProvider>
);
}
// Then use the state anywhere in your app
function ThemeControls() {
const [state, setState] = useVariationsState<GlobalState>();
return (
<div>
{/* Direct value update */}
<button
onClick={() =>
setState({
...state,
theme: { ...state.theme, primaryColor: "#000000" },
})
}
>
Set Dark Theme
</button>
{/* Updater function */}
<button
onClick={() =>
setState((prev) => ({
...prev,
theme: { ...prev.theme, primaryColor: "#ffffff" },
}))
}
>
Set Light Theme
</button>
{/* Toggle with updater function */}
<label>
<input
type="checkbox"
checked={state.preferences.showAdvancedOptions}
onChange={(e) => {
setState((prev) => ({
...prev,
preferences: {
...prev.preferences,
showAdvancedOptions: e.target.checked,
},
}));
}}
/>
Show Advanced Options
</label>
</div>
);
}
The global state feature provides:
A hook for managing a single variation group. This is the recommended way to programmatically interact with variations.
const { active, setActive, variations } = useVariation("theme");
// active: { id: string; label: string } | null
// The currently active variation or null if none is selected
console.log(active?.label); // e.g. "Light Theme"
// variations: Array<{ id: string; label: string }>
// All available variations for this group
console.log(variations); // e.g. [{ id: 'light', label: 'Light Theme' }, ...]
// setActive: (id: string) => void
// Function to change the active variation
setActive("dark");
Example usage with a select element:
function ThemeSelector() {
const { active, setActive, variations } = useVariation("theme");
return (
<select
value={active?.id || ""}
onChange={(e) => setActive(e.target.value)}
>
{variations.map(({ id, label }) => (
<option
key={id}
value={id}
>
{label}
</option>
))}
</select>
);
}
A lower-level hook that provides full access to the variations context. Use this if you need more control over variations or need to work with multiple groups at once.
const {
// Map of group IDs to their active variation IDs
activeIds,
// Function to set the active variation for a group
setActiveId,
// Map of all registered variations
variations,
// Tree representation of active variations
activeTree,
} = useVariations();
// Example: Get active variation for a group
const activeThemeId = activeIds.get("theme");
// Example: Set active variation
setActiveId("theme", "dark");
// Example: Get all variations for a group
const themeVariations = Array.from(variations.entries()).filter(
([, variation]) => variation.group === "theme"
);
For TypeScript users, you can make the hook type-safe by providing group and ID types:
type Groups = "theme" | "layout" | "typography";
type Ids = "light" | "dark" | "sidebar" | "topnav";
const { activeIds, setActiveId } = useVariations<Groups, Ids>();
// Now TypeScript will ensure you only use valid group and variation IDs
Contributions are welcome! Please feel free to submit a Pull Request.
MIT © Dan Perrera
The Variations library includes a built-in state management system that lives alongside your variations. This state is:
// First, define your state type (optional but recommended)
interface AppState {
theme: {
primaryColor: string;
fontSize: number;
};
settings: {
showAdvancedFeatures: boolean;
};
}
// Initialize the state in your root component
function App() {
const initialState: AppState = {
theme: {
primaryColor: "#000000",
fontSize: 16,
},
settings: {
showAdvancedFeatures: false,
},
};
return (
<VariationsProvider initialState={initialState}>
<YourApp />
</VariationsProvider>
);
}
// Then use the state anywhere in your app
function ThemeControls() {
const { state, setState } = useVariationsState<AppState>();
// Read from state
const { primaryColor, fontSize } = state.theme;
// Update a single value
const updateColor = (color: string) => {
setState((prev) => ({
...prev,
theme: {
...prev.theme,
primaryColor: color,
},
}));
};
// Update multiple values at once
const updateTheme = (color: string, size: number) => {
setState((prev) => ({
...prev,
theme: {
...prev.theme,
primaryColor: color,
fontSize: size,
},
}));
};
return (
<div>
<input
type="color"
value={primaryColor}
onChange={(e) => updateColor(e.target.value)}
/>
<input
type="number"
value={fontSize}
onChange={(e) => updateTheme(primaryColor, Number(e.target.value))}
/>
</div>
);
}
The state is automatically synchronized with the URL, making it easy to share specific states:
// This URL includes both variations and state
//localhost:3000/?var=theme.dark&state=%7B%22theme%22%3A%7B%22primaryColor%22%3A%22%23000%22%7D%7D
// You can disable URL synchronization if needed
http: <VariationsProvider disableQueryString={true}>
<YourApp />
</VariationsProvider>;
The state management works great alongside React's Context API:
// Create a custom hook that combines variations state with context
function useTheme() {
const { state, setState } = useVariationsState<AppState>();
const updateTheme = useCallback(
(color: string) => {
setState((prev) => ({
...prev,
theme: {
...prev.theme,
primaryColor: color,
},
}));
},
[setState]
);
return {
primaryColor: state.theme.primaryColor,
updateTheme,
};
}
// Use it in your components
function ThemeButton() {
const { primaryColor, updateTheme } = useTheme();
return <button style={{ backgroundColor: primaryColor }}>Click Me</button>;
}
FAQs
A React component library that enables rapid prototyping and exploration of UI variations.
We found that variations demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.