Switch
Pattern matching and multi-way conditional rendering for declarative multi-condition logic.
The Switch component provides a declarative pattern-matching approach for rendering content based on multiple conditions, similar to switch statements in JavaScript.
Installation
npm install @zayne-labs/ui-reactPreview
Loading...
Usage
import { Switch } from "@zayne-labs/ui-react/common/switch";
function StatusIndicator({ status }) {
return (
<Switch.Root value={status}>
<Switch.Match when="loading">Loading...</Switch.Match>
<Switch.Match when="success">Success!</Switch.Match>
<Switch.Match when="error">Error occurred</Switch.Match>
<Switch.Default>Unknown status</Switch.Default>
</Switch.Root>
);
}Component Source
"use client";import { getRegularChildren, getSingleSlot } from "@zayne-labs/toolkit-react/utils";import { isFunction } from "@zayne-labs/toolkit-type-helpers";type ValidSwitchComponentType = React.ReactElement<SwitchMatchProps<unknown>>;export type SwitchRootProps<TValue> = { children: ValidSwitchComponentType | ValidSwitchComponentType[]; value?: TValue;};const defaultValueSymbol = Symbol("default-value");// TODO - Add a factory to make this 'when' and 'value' type-safe later following the link below// LINK - https://tkdodo.eu/blog/building-type-safe-compound-components#component-factory-patternexport function SwitchRoot<TValue>(props: SwitchRootProps<TValue>) { const { children, value = defaultValueSymbol } = props; const defaultCase = getSingleSlot(children, SwitchDefault, { errorMessage: "Only one <Switch.Default> component is allowed", throwOnMultipleSlotMatch: true, }); const childrenCasesArray = getRegularChildren(children, SwitchDefault) as ValidSwitchComponentType[]; const matchedCase = childrenCasesArray.find((child) => { // == If value is defaultValueSymbol, match the cases in order like switch(true) if (value === defaultValueSymbol) { return Boolean(child.props.when); } // == Otherwise, match the cases like switch(value) return child.props.when === value; }); return matchedCase ?? defaultCase;}export type SwitchMatchProps<TWhen> = { children: React.ReactNode | ((value: TWhen) => React.ReactNode); when: false | TWhen | null | undefined;};export function SwitchMatch<TWhen>(props: SwitchMatchProps<TWhen>) { const { children, when } = props; const resolvedChildren = isFunction(children) ? children(when as TWhen) : children; return resolvedChildren;}export function SwitchDefault({ children }: { children: React.ReactNode }) { return children;}SwitchDefault.slotSymbol = Symbol("switch-default");Component API
Examples
Value Matching
In value matching mode, you provide a value to Switch.Root, and each Switch.Match checks its when prop against that value.
<Switch.Root value={status}>
<Switch.Match when="loading">Loading...</Switch.Match>
<Switch.Match when="success">Success!</Switch.Match>
<Switch.Match when="error">Error occurred</Switch.Match>
<Switch.Default>Unknown status</Switch.Default>
</Switch.Root>Boolean Matching
Omit the value prop to use boolean matching. It acts like a switch(true) block, rendering the first component whose when condition evaluates to truthy.
<Switch.Root>
<Switch.Match when={isLoading}>Loading...</Switch.Match>
<Switch.Match when={error}>Error: {error.message}</Switch.Match>
<Switch.Match when={user}>{(data) => <div>Welcome, {data.name}!</div>}</Switch.Match>
<Switch.Default>Please log in</Switch.Default>
</Switch.Root>Complex Layout Switching
<Switch.Root value={viewMode}>
<Switch.Match when="grid">
<div className="grid grid-cols-3 gap-4">{/* ... */}</div>
</Switch.Match>
<Switch.Match when="list">
<div className="flex flex-col gap-2">{/* ... */}</div>
</Switch.Match>
<Switch.Default>
<p>Select a view mode.</p>
</Switch.Default>
</Switch.Root>Only the first matching Switch.Match is rendered. This prevents the "multiple truth" problem common
when stacking ternary operators.
Last updated on