Workspace Switcher
Segmented workspace picker for switching between canvas views and operational contexts. Install Workspace Switcher from the VLLNT UI registry with the shadcn CLI.
Segmented workspace picker for switching between canvas views and operational contexts. Part of the navigation family in VLLNT UI, it ships as a machine-readable registry entry — copy the source directly into your app with the shadcn CLI and own it, no runtime dependency on a component library.
Preview
Switch between light and dark to inspect the embedded Storybook preview.
Installation
Add Workspace Switcher to your project with the shadcn CLI. The source lands in your codebase, ready to adapt:
pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/workspace-switcher.jsonSource
"use client";
import { useMemo, useState } from "react";
import { cn } from "../../lib/utils";
export type WorkspaceOption = {
description?: string;
id: string;
label: string;
};
export type WorkspaceSwitcherProps = Omit<
React.ComponentPropsWithoutRef<"div">,
"defaultValue" | "onChange"
> & {
defaultValue?: string;
onValueChange?: (value: string) => void;
value?: string;
workspaces: WorkspaceOption[];
};
const WorkspaceSwitcher = ({
className,
defaultValue,
onValueChange,
ref,
value,
workspaces,
...props
}: WorkspaceSwitcherProps & { ref?: React.Ref<HTMLDivElement> }) => {
const fallbackValue = defaultValue ?? workspaces[0]?.id ?? "";
const [internalValue, setInternalValue] = useState(fallbackValue);
const currentValue = value ?? internalValue;
const currentWorkspace = useMemo(
() => workspaces.find((workspace) => workspace.id === currentValue),
[currentValue, workspaces],
);
function handleSelect(nextValue: string) {
if (value === undefined) {
setInternalValue(nextValue);
}
onValueChange?.(nextValue);
}
return (
<div
className={cn(
"inline-flex min-w-0 items-center gap-1 rounded-full border border-border/70 bg-muted/50 p-1",
className,
)}
ref={ref}
role="radiogroup"
{...props}
>
{workspaces.map((workspace) => {
const isActive = workspace.id === currentValue;
return (
<button
aria-checked={isActive}
className={cn(
"rounded-full px-3 py-1.5 text-sm font-medium transition-colors",
isActive
? "bg-background text-foreground shadow-sm"
: "text-muted-foreground hover:text-foreground",
)}
key={workspace.id}
onClick={() => {
handleSelect(workspace.id);
}}
role="radio"
title={workspace.description}
type="button"
>
{workspace.label}
</button>
);
})}
{currentWorkspace?.description ? (
<span className="hidden pl-2 pr-1 text-xs text-muted-foreground md:inline">
{currentWorkspace.description}
</span>
) : null}
</div>
);
};
WorkspaceSwitcher.displayName = "WorkspaceSwitcher";
export { WorkspaceSwitcher };
Stories
Explore every variant and state in the interactive Storybook:
Preview
Switch between light and dark to inspect the embedded Storybook preview.