Workspace Switcher
- Stable
- WCAG 2.2 evidence
- RTL
Switches the active workspace from the top of a Sidebar: the current workspace's avatar,
name and plan open a menu that lists every workspace. Built on the library Menu (React Aria MenuTrigger + Menu):
the workspaces are a single-selection section, so the current one is announced as checked (menuitemradio), and extra
actions such as "Create workspace" can follow in the same menu, which a listbox could not hold. Enter, Space and the
arrow keys open it, arrows, Home/End and type-ahead move, Escape closes and returns focus (WCAG 2.1.1, 2.4.3, 4.1.2).
The trigger's name is the visible text plus switchLabel (2.5.3). In the collapsed rail only the avatar shows and a
tooltip gives the workspace name; the trigger stays at least 40 x 48 px (2.5.8). Consumer duties: what switching does (navigate, reload data) in onSelectionChange; wording of extra items.
import { WorkspaceSwitcher } from "@nexera-ui/react";- 5
- examples
- 30
- props
- 6
- live controls
- 1
- platform
- 8
- WCAG criteria
- 2
- blocks use it
Try every prop. Copy the code.
Change the props and the code updates. Check light and dark, LTR and RTL, and phone width.
import { WorkspaceSwitcher } from "@nexera-ui/react";
<WorkspaceSwitcher />Examples 4
The same examples as Storybook, rendered live. Open Code to copy one.
Menu open with actions
The menu open: workspaces as single-choice items, then extra actions after a divider.
import { Sidebar, WorkspaceSwitcher } from "@nexera-ui/react";
const workspaces = [
{ id: "pk", name: "Acme Pakistan", plan: "Business plan" },
{ id: "uk", name: "Acme UK", plan: "Starter plan" },
{ id: "de", name: "Acme Deutschland", plan: "Business plan" },
];
export function MenuOpenWithActions() {
return (
<div className="min-h-[22rem]">
<Sidebar
aria-label="Sidebar"
className="h-auto"
header={<WorkspaceSwitcher workspaces={workspaces} />}
/>
</div>
);
}
Variants and rail
Figma Variant=Light and Collapsed=True (avatar only; focus or hover it for the name).
import { Sidebar, WorkspaceSwitcher } from "@nexera-ui/react";
const workspaces = [
{ id: "pk", name: "Acme Pakistan", plan: "Business plan" },
{ id: "uk", name: "Acme UK", plan: "Starter plan" },
{ id: "de", name: "Acme Deutschland", plan: "Business plan" },
];
export function VariantsAndRail() {
return (
<div className="flex flex-wrap items-start gap-4">
{(["brand", "light"] as const).flatMap((variant) =>
[false, true].map((isCollapsed) => (
<Sidebar
key={`${variant}${String(isCollapsed)}`}
variant={variant}
isCollapsed={isCollapsed}
aria-label={`${variant} ${isCollapsed ? "rail" : "sidebar"}`}
className="h-auto"
header={<WorkspaceSwitcher workspaces={workspaces} />}
/>
)),
)}
</div>
);
}
Long name
Long workspace names wrap.
import { WorkspaceSwitcher } from "@nexera-ui/react";
<WorkspaceSwitcher
workspaces={[{ id: "x", name: "Acme Human Resources Holding Pakistan", plan: "Enterprise plan" }]}
/>;
Right to left
Right to left.
import { WorkspaceSwitcher } from "@nexera-ui/react";
const workspaces = [
{ id: "pk", name: "Acme Pakistan", plan: "Business plan" },
{ id: "uk", name: "Acme UK", plan: "Starter plan" },
{ id: "de", name: "Acme Deutschland", plan: "Business plan" },
];
<WorkspaceSwitcher workspaces={workspaces} />;
Props 30
Press "Try it" on a card to load that prop into the playground.
30 props shown
workspaces*Nexerareadonly Workspace[]The workspaces, listed in the menu as single-choice items (menuitemradio); the chosen one fills the trigger.
selectedKeyNexeraKeyKey of the current workspace (controlled). Use with onSelectionChange.
defaultSelectedKeyNexeraKeyKey of the current workspace on first render (uncontrolled).
onSelectionChangeNexera(key: Key) => voidCalled with the key of the workspace the user switches to.
childrenNexeraReactNodeExtra menu items after a divider, for example "Create workspace" or "Workspace settings" (MenuItem elements).
Their activation is reported by onAction.
onActionNexera(key: Key) => voidCalled with the id of an extra item (from children) the user activates.
switchLabelNexerastringVisually hidden text added to the trigger's accessible name after the visible workspace and plan, so the purpose is announced ("Acme Pakistan Business plan Switch workspace"). Translate it.
variantNexera"brand" | "light"Colour scheme.
isCollapsedNexerabooleanShow only the avatar; the workspace name moves to a tooltip.
isOpenNexerabooleanWhether the menu is open (controlled). Use with onOpenChange.
defaultOpenNexerabooleanWhether the menu is open on first render (uncontrolled).
onOpenChangeNexera(isOpen: boolean) => voidCalled when the menu opens or closes.
placementNexera"bottom" | "bottom left" | "bottom right" | "bottom start" | "bottom end" | "top" | "top left" | "top right" | "top start" | "top end" | "left" | "left top" | "left bottom" | "start" | "start top" | "start bottom" | "right" | "right top" | "right bottom" | "end" | "end top" | "end bottom"Where the menu opens. It flips to stay in the viewport.
classNameNexerastringExtra classes for the trigger, merged last so they win over the defaults.
styleNexeraCSSPropertiesInline style for the trigger.
isDisabledReact AriabooleanWhether the button is disabled.
onPressReact Aria(e: PressEvent) => voidHandler that is called when the press is released over the target.
onPressStartReact Aria(e: PressEvent) => voidHandler that is called when a press interaction starts.
onPressEndReact Aria(e: PressEvent) => voidHandler that is called when a press interaction ends, either over the target or when the pointer leaves the target.
onPressChangeReact Aria(isPressed: boolean) => voidHandler that is called when the press state changes.
onPressUpReact Aria(e: PressEvent) => voidHandler that is called when a press is released over the target, regardless of whether it started on the target or not.
autoFocusReact AriabooleanWhether the element should receive focus on render.
typeReact Aria"button" | "submit" | "reset"The behavior of the button when used in an HTML form.
formReact AriastringThe <form> element to associate the button with.
The value of this attribute must be the id of a <form> in the same document.
See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/button#form).
nameReact AriastringSubmitted as a pair with the button's value as part of the form data.
valueReact AriastringThe value associated with the button's name when it's submitted with the form data.
idReact AriastringThe element's unique identifier. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id).
aria-labelReact AriastringDefines a string value that labels the current element.
aria-labelledbyReact AriastringIdentifies the element (or elements) that labels the current element.
aria-describedbyReact AriastringIdentifies the element (or elements) that describes the object.
* Required. React Aria props shown are the ones most apps use; the component accepts the rest of its React Aria props too.
Accessible by default.
Built on React Aria, and covered by the WCAG 2.2 evidence generated on every build.
WCAG 2.2 evidence
6 direct · 2 supporting- 1.3.1Info and RelationshipsLevel A · tested directly
- 1.4.12Text SpacingLevel AA · supporting test
- 1.4.13Content on Hover or FocusLevel AA · tested directly
- 2.1.1KeyboardLevel A · tested directly
- 2.4.3Focus OrderLevel A · tested directly
- 2.4.7Focus VisibleLevel AA · supporting test
- 2.5.3Label in NameLevel A · tested directly
- 4.1.2Name, Role, ValueLevel A · tested directly
Fits any width.
Nexera components respond to the space they are given. Drag the corner of the frame, or pick a width.
Styling hooks
Pass className to add Tailwind classes (merged last). State is exposed as data attributes, so you can style it with variants like data-pressed:.
data-disableddata-hovereddata-pressed
<WorkspaceSwitcher className="data-disabled:opacity-90 shadow-sm" />Used in blocks
Related components
- BreadcrumbShows where the current page sits in the hierarchy and links back to each level.
- BreadcrumbItemOne level of a `Breadcrumb` trail; use it only inside `Breadcrumb`.
- CursorPagerA previous and next pair for data without stable page numbers, such as an activity log or a cursor API.
- FABThe main action of a mobile screen: a floating button with a `nav/active-bg` fill and a large shadow.
- FeedStatusThe status line at the bottom of an infinite feed or a lazy list: "Loading more", "You have reached the end" or "Could not load more" with a retry action.
- JumpToPageA small "Go to page [48] of 120" control for long paged lists.
- NavigationBarThe bottom bar of a mobile layout: a named `nav` landmark with a list of three to five `NavItem`s.
- NavItemOne destination or action of a `NavigationBar`.