Organization Switcher
Switch organisations with plan context and a workspace creation adapter.
Make it yours.
Install the editable source and its dependencies into your React + Tailwind project.
Scroll horizontally for long lines
pnpm dlx shadcn@4.0.8 add http://localhost:3000/r/organization-switcher.jsonUse it
Scroll horizontally for long lines
"use client";
import {OrganizationSwitcher} from '@/components/jez-ui/blocks/organization-switcher';
export default function Example(){
return <><OrganizationSwitcher/></>;
}Props & types
Scroll horizontally for long lines
export type Organization = { id: string; name: string; plan: string };
{
items?: Organization[];
onValueChange?: (id: string) => void;
onCreate?: () => void;
compact?: boolean;
}Source
View 1 source file
registry/blocks/organization-switcher.tsx
Scroll horizontally for long lines
"use client";
import * as React from "react";
import { DropdownMenu as D } from "radix-ui";
import { Check, ChevronsUpDown, Plus } from "lucide-react";
export type Organization = { id: string; name: string; plan: string };
const defaults: Organization[] = [
{ id: "common", name: "Common Studio", plan: "Team workspace" },
{ id: "personal", name: "Personal", plan: "Free workspace" },
{ id: "fieldwork", name: "Fieldwork", plan: "Team workspace" },
];
export function OrganizationSwitcher({
items = defaults,
onValueChange,
onCreate,
compact = false,
}: {
items?: Organization[];
onValueChange?: (id: string) => void;
onCreate?: () => void;
compact?: boolean;
}) {
const [value, setValue] = React.useState(items[0]?.id);
const [notice, setNotice] = React.useState("");
const active = items.find((i) => i.id === value) ?? items[0];
if (!active) return null;
return (
<div>
<D.Root>
<D.Trigger
aria-label={`Workspace: ${active.name}`}
className="flex w-full items-center gap-3 rounded-lg p-2 text-left hover:bg-muted"
>
<span className="grid size-9 shrink-0 place-items-center rounded-lg bg-primary font-display text-lg text-primary-foreground">
{active.name[0]}
</span>
{!compact && (
<>
<span className="min-w-0 flex-1">
<span className="block truncate text-sm font-semibold">
{active.name}
</span>
<span className="block text-xs text-muted-foreground">
{active.plan}
</span>
</span>
<ChevronsUpDown size={14} />
</>
)}
</D.Trigger>
<D.Portal>
<D.Content
sideOffset={8}
align="start"
collisionPadding={12}
className="jez-popover z-50 w-64 rounded-xl border border-border bg-background p-1.5 text-foreground shadow-xl"
>
<D.Label className="px-3 py-2 text-xs text-muted-foreground">
Your workspaces
</D.Label>
<D.RadioGroup
value={active.id}
onValueChange={(id) => {
setValue(id);
onValueChange?.(id);
}}
>
{items.map((item) => (
<D.RadioItem
key={item.id}
value={item.id}
className="flex items-center justify-between gap-3 rounded-md p-3 text-sm outline-none data-[highlighted]:bg-muted"
>
<span>
{item.name}
<span className="mt-1 block text-xs text-muted-foreground">
{item.plan}
</span>
</span>
<D.ItemIndicator>
<Check size={14} />
</D.ItemIndicator>
</D.RadioItem>
))}
</D.RadioGroup>
<D.Separator className="my-1 h-px bg-border" />
<D.Item
onSelect={() =>
onCreate
? onCreate()
: setNotice("Demo: connect your workspace creation flow.")
}
className="flex items-center gap-2 rounded-md p-3 text-sm outline-none data-[highlighted]:bg-muted"
>
<Plus size={15} />
Create workspace
</D.Item>
</D.Content>
</D.Portal>
</D.Root>
{notice && (
<p role="status" className="p-2 text-xs">
{notice}
</p>
)}
</div>
);
}
Dependencies
radix-ui@1.6.7 · lucide-react@0.577.0
Accessibility & behaviour
Provide meaningful labels and preserve keyboard focus styles. Check contrast when customising theme colours.
Read the accessibility and performance guide →