Components · Identity
WorkspaceSwitcher
Switch between the workspaces a person belongs to, with search and Create workspace.
Switch, create and manage
import { useState } from 'react';
import { WorkspaceSwitcher } from '@rookcom/monarch-react/shell';
const workspaces = [
{ id: 'w1', name: 'Acme', handle: 'acme' },
{ id: 'w2', name: 'Northfield Traders', handle: 'northwind' },
{ id: 'w3', name: 'Globex', handle: 'globex', keywords: 'globex corporation' },
];
function Basic() {
const [workspace, setWorkspace] = useState('w1');
return (
<div style={{ width: '15rem' }}>
<WorkspaceSwitcher
workspaces={workspaces}
value={workspace}
onValueChange={setWorkspace}
onCreate={() => {}}
manageHref="https://id.example.com/workspaces"
/>
</div>
);
}
<Basic />Usage examples
Collapsed, and nothing chosen yet
import { WorkspaceSwitcher } from '@rookcom/monarch-react/shell';
const workspaces = [
{ id: 'w1', name: 'Acme', handle: 'acme' },
{ id: 'w2', name: 'Northfield Traders', handle: 'northwind' },
{ id: 'w3', name: 'Globex', handle: 'globex', keywords: 'globex corporation' },
];
function Compact() {
return (
<div style={{ display: 'flex', gap: 'var(--mn-space-3)', alignItems: 'center' }}>
<WorkspaceSwitcher workspaces={workspaces} defaultValue="w2" collapsed />
<div style={{ width: '15rem' }}>
<WorkspaceSwitcher workspaces={workspaces} createHref="#new-workspace" />
</div>
</div>
);
}
<Compact />Purpose
Every Rook app lets a person move between their workspaces from the rail. One shared control names its trigger, marks the current workspace for screen readers and supports search.
Use when
- The rail’s
workspaceSwitcherslot, or the top of a settings sidebar.
Avoid when
- Choosing a Space inside the workspace: use SpaceSwitcher.
- Picking a workspace as a form value (inviting to a workspace): use EntityPicker in a Field.
Design principles
- The app does the switch:
onValueChangegets the id and the app navigates (and may keep the sub-page). - Square avatars for workspaces (round ones are people), with initials when there is no logo.
- The current workspace is
aria-selected, never marked by a coloured dot alone. - Search matches names, handles and hidden keywords such as old names.
API reference
import { WorkspaceSwitcher } from '@rookcom/monarch-react/shell';| Export | Kind | Rendering | Root class |
|---|---|---|---|
WorkspaceSwitcher | component | client | .mn-workspace-switcher |
Forwards its ref to the trigger button. Built on EntityPicker and Avatar from @rookcom/monarch-react.
WorkspaceOption is { id, name, handle?, avatarUrl?, keywords? }.
Generated from the TypeScript declarations of packages/react/src/components/identity/workspace-switcher.
Properties
Controlled with value + onValueChange, or uncontrolled with defaultValue. There is no "none" choice.
onCreate or createHref adds "Create workspace"; manageHref adds "All workspaces" (new tab unless manageExternal={false}).
WorkspaceSwitcher
Switches between the workspaces a person belongs to: an avatar trigger (name and handle when there is room), a searchable popover with the current workspace checked, and Create / All workspaces actions. Keyboard: the listbox model of EntityPicker (arrows, Home/End, type to search, Enter, Escape returns focus). The app supplies the workspaces and does the switch.
| Prop | Type | Description |
|---|---|---|
workspaces required | WorkspaceOption[] | The workspaces this person belongs to. |
align | "center" | "start" | "end" | |
className | string | |
collapsed | boolean | Avatar-only trigger for a collapsed rail; the name moves into a tooltip. |
createHref | string | …or a link. |
defaultValue | string | null | |
loading | boolean | |
manageExternal | boolean | Open `manageHref` in a new tab. Default true. |
manageHref | string | "All workspaces" link (usually Rook ID). |
messages | Partial<WorkspaceSwitcherMessages> | |
onCreate | (() => void) | "Create workspace": an action (the popover closes first)… |
onOpenChange | ((open: boolean) => void) | |
onValueChange | ((workspaceId: string) => void) | Called with the chosen workspace id. The app navigates (and keeps the sub-page if it wants). |
open | boolean | |
value | string | null | The current workspace id (controlled). |
Variants
| Prop | Values | Description |
|---|---|---|
collapsed | 'false''true' | Avatar-only trigger with the name in a tooltip, for the 48px rail. |
States
| State | Exposed as | Behaviour |
|---|---|---|
| Current workspace | aria-selected="true" | Checked in the list; the trigger shows its name and @handle. |
| Nothing chosen | – | The trigger reads "Workspace: Choose a workspace" with a neutral icon. |
| Loading | – | The list says "Loading workspaces…". |
| Open | aria-expanded="true" | On the trigger. |
Interaction behaviour
- Opening moves focus to the search box; choosing closes the popover and returns focus to the trigger.
- Choosing the current workspace again does nothing.
- "Create workspace" closes the popover first, then runs
onCreate(open your dialog there).
Keyboard support
| Keys | Action |
|---|---|
| Enter / Space / ↓ | Open from the trigger. |
| ↓ / ↑ / Home / End | Move through the workspaces. |
| Typing | Filter by name, handle or keyword. |
| Enter | Switch to the highlighted workspace. |
| Escape | Close and return focus to the trigger. |
Accessibility
- Role
- button opening a dialog with a search combobox and a listbox
- Accessible name
- The trigger is named by its text, "Workspace: <name> @handle" (
messages.labelis visually hidden), also when collapsed to an avatar.
- Fixes the audited gaps: a collapsed trigger without a name and an active item shown by colour only.
- Avatars are decorative; the option label and @handle name each workspace.
- The All workspaces link announces the new tab.
Responsive behaviour
- The expanded trigger fills its container and truncates long names; the popover never exceeds the screen width.
Theme support
- Tokens only; inside RookRail the trigger uses the accent panel colours.
- Transitions are removed under reduced motion.
Do and don't
Do
- Keep the sub-page when switching if the page exists in the other workspace.
- Pass workspace logos as
avatarUrl; initials are the fallback.
Don't
- Do not list workspaces the person cannot open.
- Do not put Space choices in this list.
Related components
- SpaceSwitcher: The Space inside the workspace.
- EntityPicker: The picker it is built on.
- Avatar: Workspace logos and initials.
- RookRail: Has a slot for it.
Dependencies
| Kind | Depends on |
|---|---|
| npm packages | react |
| Monarch components | Avatar, EntityPicker |
Testing coverage
- Trigger named with the current workspace and its handle.
- List with the current one selected; switching calls
onValueChangeand returns focus. - Keyboard search over names, handles and keywords; re-choosing the current one is ignored; controlled mode.
- Create workspace (action and link) and All workspaces; placeholder and empty message; collapsed tooltip.
| Check | Covered |
|---|---|
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__gallery/workspace-switcher; Cross-browser axe: /components/workspace-switcher |
Tests: packages/react/src/components/identity/workspace-switcher