Skip to main content
The Switch component provides a declarative pattern-matching approach for rendering content based on multiple conditions, similar to switch statements in JavaScript.

When to Use

  • Render different content based on multiple mutually exclusive conditions
  • Implement pattern matching in React components
  • Replace long chains of if-else or ternary operators
  • Handle state machines with different states
  • Provide default fallback for unmatched cases

Basic 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 API

Switch.Root

Container that evaluates and renders the first matching case.
value
TValue
Value to match against. If omitted, switches to boolean matching mode (like switch(true))
children
SwitchMatchElement | SwitchMatchElement[]
required
Switch.Match and Switch.Default components to evaluate

Switch.Match

Defines a case to match against.
when
TWhen | false | null | undefined
required
Value to match (in value mode) or condition to evaluate (in boolean mode)
children
React.ReactNode | ((value: TWhen) => React.ReactNode)
required
Content to render when matched. Can be a render function receiving the matched value

Switch.Default

Defines fallback content when no cases match.
children
React.ReactNode
required
Fallback content to display

Examples

API Response States

import { Switch } from "@zayne-labs/ui-react/common/switch";

function ApiResponse({ status, data, error }) {
  return (
    <Switch.Root value={status}>
      <Switch.Match when="idle">
        <div>Ready to fetch data</div>
      </Switch.Match>

      <Switch.Match when="loading">
        <div className="spinner">Loading...</div>
      </Switch.Match>

      <Switch.Match when="success">
        <div className="data">
          <pre>{JSON.stringify(data, null, 2)}</pre>
        </div>
      </Switch.Match>

      <Switch.Match when="error">
        <div className="error">
          <h3>Error occurred</h3>
          <p>{error?.message}</p>
        </div>
      </Switch.Match>

      <Switch.Default>
        <div>Unknown state</div>
      </Switch.Default>
    </Switch.Root>
  );
}

User Role Rendering

import { Switch } from "@zayne-labs/ui-react/common/switch";

function Dashboard({ user }) {
  return (
    <Switch.Root value={user.role}>
      <Switch.Match when="admin">
        <AdminDashboard user={user} />
      </Switch.Match>

      <Switch.Match when="moderator">
        <ModeratorDashboard user={user} />
      </Switch.Match>

      <Switch.Match when="user">
        <UserDashboard user={user} />
      </Switch.Match>

      <Switch.Default>
        <GuestDashboard />
      </Switch.Default>
    </Switch.Root>
  );
}

Complex Condition Matching

import { Switch } from "@zayne-labs/ui-react/common/switch";

function PricingCard({ user, plan }) {
  const isPremium = plan === 'premium';
  const isExpired = new Date(user.expiresAt) < new Date();
  const isFreeTrial = user.isTrial;

  return (
    <Switch.Root>
      <Switch.Match when={isExpired}>
        <div className="expired">
          <h3>Subscription Expired</h3>
          <button>Renew Now</button>
        </div>
      </Switch.Match>

      <Switch.Match when={isFreeTrial}>
        <div className="trial">
          <h3>Free Trial Active</h3>
          <p>Upgrade to unlock all features</p>
        </div>
      </Switch.Match>

      <Switch.Match when={isPremium}>
        <div className="premium">
          <h3>Premium Member</h3>
          <p>Thank you for your support!</p>
        </div>
      </Switch.Match>

      <Switch.Default>
        <div className="free">
          <h3>Free Plan</h3>
          <button>Upgrade to Premium</button>
        </div>
      </Switch.Default>
    </Switch.Root>
  );
}

Theme Selector

import { Switch } from "@zayne-labs/ui-react/common/switch";

function ThemePreview({ theme }) {
  return (
    <Switch.Root value={theme}>
      <Switch.Match when="light">
        <div className="preview-light">
          <h3>Light Theme</h3>
          <p>Clean and bright interface</p>
        </div>
      </Switch.Match>

      <Switch.Match when="dark">
        <div className="preview-dark">
          <h3>Dark Theme</h3>
          <p>Easy on the eyes</p>
        </div>
      </Switch.Match>

      <Switch.Match when="auto">
        <div className="preview-auto">
          <h3>Auto Theme</h3>
          <p>Matches system preference</p>
        </div>
      </Switch.Match>

      <Switch.Default>
        <div>Custom theme</div>
      </Switch.Default>
    </Switch.Root>
  );
}

With Render Functions

import { Switch } from "@zayne-labs/ui-react/common/switch";

function NotificationBadge({ type, count }) {
  return (
    <Switch.Root value={type}>
      <Switch.Match when="message">
        {(matchedType) => (
          <div className="badge badge-blue">
            <MessageIcon />
            {count > 0 && <span>{count}</span>}
          </div>
        )}
      </Switch.Match>

      <Switch.Match when="alert">
        {(matchedType) => (
          <div className="badge badge-red">
            <AlertIcon />
            {count > 0 && <span>{count}</span>}
          </div>
        )}
      </Switch.Match>

      <Switch.Match when="info">
        {(matchedType) => (
          <div className="badge badge-gray">
            <InfoIcon />
            {count > 0 && <span>{count}</span>}
          </div>
        )}
      </Switch.Match>

      <Switch.Default>
        <div className="badge">Unknown</div>
      </Switch.Default>
    </Switch.Root>
  );
}

Order Status Flow

import { Switch } from "@zayne-labs/ui-react/common/switch";

function OrderStatus({ order }) {
  return (
    <div className="order-status">
      <Switch.Root value={order.status}>
        <Switch.Match when="pending">
          <div className="status-pending">
            <ClockIcon />
            <span>Order Pending</span>
            <p>Waiting for confirmation</p>
          </div>
        </Switch.Match>

        <Switch.Match when="processing">
          <div className="status-processing">
            <SpinnerIcon />
            <span>Processing</span>
            <p>Preparing your order</p>
          </div>
        </Switch.Match>

        <Switch.Match when="shipped">
          <div className="status-shipped">
            <TruckIcon />
            <span>Shipped</span>
            <p>Tracking: {order.trackingNumber}</p>
          </div>
        </Switch.Match>

        <Switch.Match when="delivered">
          <div className="status-delivered">
            <CheckIcon />
            <span>Delivered</span>
            <p>Delivered on {order.deliveredAt}</p>
          </div>
        </Switch.Match>

        <Switch.Match when="cancelled">
          <div className="status-cancelled">
            <XIcon />
            <span>Cancelled</span>
            <p>Order was cancelled</p>
          </div>
        </Switch.Match>

        <Switch.Default>
          <div className="status-unknown">
            Unknown status
          </div>
        </Switch.Default>
      </Switch.Root>
    </div>
  );
}

Comparison to Native Patterns

Multiple Ternaries

function StatusIndicator({ status }) {
  return (
    <div>
      {status === "loading" ? (
        <div>Loading...</div>
      ) : status === "success" ? (
        <div>Success!</div>
      ) : status === "error" ? (
        <div>Error occurred</div>
      ) : (
        <div>Unknown status</div>
      )}
    </div>
  );
}

If-Else Chain

function StatusIndicator({ status }) {
  let content;

  if (status === "loading") {
    content = <div>Loading...</div>;
  } else if (status === "success") {
    content = <div>Success!</div>;
  } else if (status === "error") {
    content = <div>Error occurred</div>;
  } else {
    content = <div>Unknown status</div>;
  }

  return <div>{content}</div>;
}

With Switch Component

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>
  );
}

Common Use Cases

Form Step Navigation

import { Switch } from "@zayne-labs/ui-react/common/switch";

function MultiStepForm({ currentStep }) {
  return (
    <Switch.Root value={currentStep}>
      <Switch.Match when={1}>
        <PersonalInfoStep />
      </Switch.Match>
      <Switch.Match when={2}>
        <AddressStep />
      </Switch.Match>
      <Switch.Match when={3}>
        <PaymentStep />
      </Switch.Match>
      <Switch.Match when={4}>
        <ReviewStep />
      </Switch.Match>
      <Switch.Default>
        <CompletionStep />
      </Switch.Default>
    </Switch.Root>
  );
}

File Type Preview

import { Switch } from "@zayne-labs/ui-react/common/switch";

function FilePreview({ file }) {
  const extension = file.name.split('.').pop();

  return (
    <Switch.Root value={extension}>
      <Switch.Match when="jpg">
        <ImagePreview src={file.url} />
      </Switch.Match>
      <Switch.Match when="png">
        <ImagePreview src={file.url} />
      </Switch.Match>
      <Switch.Match when="pdf">
        <PDFViewer src={file.url} />
      </Switch.Match>
      <Switch.Match when="mp4">
        <VideoPlayer src={file.url} />
      </Switch.Match>
      <Switch.Default>
        <GenericFileIcon />
        <p>Preview not available</p>
      </Switch.Default>
    </Switch.Root>
  );
}
When no value prop is provided, Switch works like switch(true) in JavaScript, evaluating each when condition until it finds a truthy one.
Only the first matching Switch.Match is rendered. Once a match is found, remaining cases are skipped.

Build docs developers (and LLMs) love