Workspace, Org & Tenant Switcher
If you're building B2B SaaS in 2026 with multi-tenancy (one user can belong to multiple orgs / workspaces / teams), users will expect a workspace switcher — UI to jump between contexts. Slack, Linear, Notion, Figma, GitHub all have one. The naive approach: dropdown that reloads the page on switch. The structured approach: persistent workspace state in URL, fast-switch via cmd+K, recently-used ordering, multi-tenant data isolation, JWT scoping per workspace, secure logout that invalidates all tenants. Workspace switching looks like a simple UI feature and is actually a deep multi-tenant architecture problem. (See multi-tenancy-chat.md for backend architecture; this guide is the user-facing UX + URL state + auth flow.)
1. Decide: workspace per session or persistent
Decide workspace state model.
Approach 1: Last-active workspace (recommended)
- User logs in → land in last-active workspace
- Switch sticks across sessions
- Each user-workspace pair has its own URL space
- Used by: Slack, Notion, Linear
Approach 2: Workspace selector on every login
- Login → choose workspace
- Used by: GitHub for Enterprise users
- Pro: explicit; safer
- Con: extra step
Approach 3: URL-driven (no default)
- User must navigate to /[workspace-slug]/...
- Workspace identity always in URL
- Used by: Vercel, Notion, GitHub
- Pro: shareable, deep-linkable
- Con: no implicit default
Hybrid (recommended for most B2B SaaS):
- Workspace slug in URL (/[workspace]/projects/...)
- Last-active workspace remembered for landing
- Direct URL bypasses default
Single-tenant exception:
- Some products are 1 user = 1 workspace (no switcher needed)
- Don't add workspace switcher unless multi-tenant
For [USE CASE], output:
1. State model recommendation
2. URL pattern
3. Login flow
4. Default behavior
5. Single-tenant fallback (if applicable)
The URL-in-slug pattern is the 2026 default: /[workspace]/projects/[id]. Bookmarks work; sharing works; auth context is explicit.
2. URL pattern — workspace slug everywhere
Design workspace URL structure.
Pattern A: Slug prefix (Linear / Notion / Vercel)
- /workspace-slug/path/to/resource
- Examples: /linear/issues/123; /myteam/dashboard
Pattern B: Subdomain
- workspace-slug.app.com/path
- Used by: Slack (slack.com → team.slack.com)
- Pro: cleaner URLs; tenant isolation visible
- Con: subdomain provisioning; SSL cert per subdomain (handled by wildcard); more setup
Pattern C: Query parameter
- /path?workspace=slug
- Pro: simple
- Con: easy to forget; prone to bugs
Recommended: Pattern A (slug prefix) for most B2B SaaS.
Slug rules:
- Lowercase
- Hyphenated
- 3-30 chars
- Reserved words excluded (admin, api, www, etc.)
- Globally unique
- Editable but with redirect from old slug
Resolution:
- Middleware extracts slug from URL
- Validates user has access to workspace
- Sets workspace context for request
- Returns 404 if not found / no access
Edge cases:
- User switches workspace → URL updates (different prefix)
- Direct URL to workspace user doesn't have → redirect to default + show error
- Renamed slug → 301 redirect from old to new
- Deleted workspace → 410 Gone
Output:
1. URL pattern
2. Slug rules
3. Middleware logic
4. Redirect / error handling
5. Slug rename support
The Next.js 16 implementation: app/[workspace]/... dynamic route + middleware to validate access. Clean; well-supported.
3. Workspace switcher UI — top bar / left sidebar
Design workspace switcher UI.
Patterns:
Top-left header dropdown (Slack, Notion):
- Workspace name + logo at top-left
- Click → list of workspaces
- "Add workspace" / "Create new" at bottom
Left sidebar (Slack desktop, Linear mobile):
- Vertical list of workspace icons
- Hover for name
- Click to switch
Cmd+K palette (Linear, Notion modern):
- Workspace switcher inside command palette
- "Switch workspace" command
- Type to filter
Footer / settings (rarely):
- Workspace selector in user menu
- Less prominent
Recommended: top-left dropdown + cmd+K integration.
Dropdown content:
- Current workspace highlighted
- List of workspaces (recent first)
- Hover/click to switch
- "Create new workspace" CTA at bottom
- "Workspace settings" link
- "Sign out" maybe (or in user menu)
Workspace icons:
- Logo if uploaded
- Letter avatar fallback (first letter)
- Color hashed from name
Sorting:
- Most recently used first
- Or alphabetical (with current pinned to top)
- Configurable user preference
Search (when many workspaces):
- Type to filter
- Required when 10+ workspaces
Mobile:
- Bottom sheet or full-screen modal (more space)
- Touch-friendly tap targets
Output:
1. UI pattern + position
2. Workspace icon strategy
3. Sorting rules
4. Search threshold
5. Mobile fallback
The cmd+K pattern is increasingly the default. Power users hit cmd+K, type workspace name, switch. Faster than any dropdown.
4. Switching mechanics — fast vs full reload
Design switch mechanics.
Approach A: Full page reload (simple)
- Click switch → window.location.href = new URL
- Server-render the new workspace
- Pro: simple; clean state reset
- Con: slow; UX feels "old"
Approach B: Client-side route change (modern)
- Update URL without reload (history.pushState / Next.js router.push)
- Refetch workspace-scoped data
- Pro: fast; SPA-feel
- Con: state cleanup harder
Approach C: Hybrid
- Use client-side navigation when possible
- Force full reload on certain transitions (e.g., different auth scope)
Cache invalidation:
- TanStack Query / SWR: invalidate workspace-scoped queries
- Persistent state (Zustand): clear / replace
- URL state: stays in URL (bookmarks work)
Workspace-scoped data:
- Projects, users, settings — all scoped to workspace
- On switch: refetch
- Don't show stale data from previous workspace
Loading state:
- Show "Switching to [workspace]..." overlay or spinner
- Most fetches < 500ms; brief loader fine
- For expensive switches: skeleton
Auth scope:
- Some apps issue per-workspace JWT (scoped tokens)
- Switch → request new token
- Backend validates token per request
Output:
1. Mechanics approach
2. Cache invalidation
3. Loading state
4. Auth scope
5. Failure handling (auth refresh fails)
The "client-side navigation + cache reset" pattern: feels instant; works with TanStack Query's queryClient.invalidateQueries({ queryKey: ['workspace', oldId] }).
5. Multi-tenant data isolation
The single most-important security requirement. Get wrong → cross-tenant data leak.
Enforce multi-tenant data isolation.
Server-side (mandatory):
Every query MUST scope by workspace_id:
- SELECT * FROM projects WHERE workspace_id = ? — required
- Without it: data leaks across tenants
Implementation patterns:
Pattern 1: Manual filtering
- Every API endpoint scopes by req.workspace.id
- Easy to forget; one bug = leak
- Audit critical
Pattern 2: ORM / DB middleware
- Drizzle / Prisma plugin auto-adds workspace_id
- Set tenant context per request
- Prevents accidental omission
Pattern 3: Postgres RLS (Row-Level Security)
- Define RLS policies on tables
- Connection sets app.current_workspace
- DB enforces; impossible to bypass
- Recommended for high-stakes multi-tenant
Pattern 4: Schema-per-tenant
- Each workspace gets own schema or DB
- Strong isolation
- Operational complexity
- For: enterprise / regulatory
Authorization (orthogonal):
- User membership in workspace (users_workspaces table)
- Roles within workspace (admin, member, viewer)
- Per-resource permissions
JWT / token scoping:
- Token includes workspace_id
- Server validates token's workspace matches request
- Different workspace = different token
Anti-patterns:
- Trust client-provided workspace_id without validation
- "WHERE id = ?" without workspace check (id is not unique across tenants)
- Log workspace data without workspace_id in logs
Output:
1. Isolation pattern
2. Code-level enforcement
3. RLS / ORM middleware setup
4. Audit checklist
5. Test cases (cross-tenant attempts must fail)
The Postgres RLS pattern is the safety net. Even if your API code has a bug, RLS prevents data leak. Setup is one-time; benefit is forever.
6. Invitation + acceptance flow
How does a new user join a workspace?
Design workspace invite flow.
Inviter side:
- Settings → Members → Invite
- Form: email + role
- Send invite (email with link)
Email content:
- "You're invited to [Workspace Name]"
- Inviter's name
- Accept link (with token)
- Expires (7 days typical)
Recipient side:
Path A: Already has account
- Click link → log in (if not) → land on workspace acceptance
- "Accept invitation" button
- After accept: workspace appears in switcher; current workspace switches to it
Path B: No account
- Click link → signup flow
- Pre-fill email
- After signup → auto-accept invite → land in workspace
Token handling:
- One-time use; consumed on accept
- Expires after 7 days
- Tied to specific email (don't allow other emails to accept)
- Revocable by inviter
Roles:
- Admin (full access)
- Member (work-level access)
- Guest (limited; specific projects only)
- Viewer (read-only)
Auto-join domain (optional):
- Same email domain → auto-join workspace
- Common in Notion / Slack / Loom
- Enable per-workspace setting
- Used for "company-wide" workspaces
Decline flow:
- Recipient declines → notify inviter
- Don't allow re-invite spam
Output:
1. Invite UI
2. Email template
3. Token mechanics
4. Acceptance flow
5. Roles + permissions
6. Auto-join domain (if applicable)
The auto-join-by-domain feature: common in B2B SaaS; should be opt-in per workspace; defaults to off (security). Owner enables when ready.
7. Workspace settings — naming, branding, billing
Design workspace settings.
Settings pages:
General:
- Workspace name
- Slug (with redirect on change)
- Logo / avatar
- Description / bio
- Timezone (default for users)
Members:
- List of members with roles
- Invite new
- Remove members
- Promote / demote
Billing:
- Plan (Free / Pro / Enterprise)
- Payment method
- Invoices
- Usage / quotas
Branding:
- Logo
- Primary color (theme)
- Custom domain (Enterprise)
- Email branding (Enterprise)
Integrations:
- Connected services (Slack, Linear, Salesforce)
- API keys / tokens
Security:
- 2FA enforcement
- SSO setup (Enterprise)
- Audit log access
Danger zone:
- Transfer ownership
- Delete workspace
Permissions:
- Most settings: admin only
- Some (display name, profile): all members
- Owner: irrevocable (one per workspace)
Multi-owner:
- Some products allow multiple owners
- Helps when owner leaves
Output:
1. Settings page structure
2. Per-page permission rules
3. Owner / admin / member differentiation
4. Critical actions (delete workspace) confirmation
5. Audit log entry on changes
The "delete workspace" guard: confirm modal with workspace name typing required ("Type 'workspace-slug' to confirm"). Otherwise accidental deletion is a real risk.
8. Switcher in command palette (cmd+K)
Power users want fast switch via keyboard.
Implement cmd+K workspace switching.
Command palette (cmdk library or kbar):
Sections:
- Quick actions (most-frequent)
- Workspaces (all user's workspaces)
- Settings
- Help
Workspace switch flow:
- User opens cmd+K
- Types "switch" or workspace name
- Filters list
- Enter → switch
Implementation:
import { Command } from 'cmdk';
<Command.Dialog>
<Command.Input placeholder="Search..." />
<Command.List>
<Command.Group heading="Workspaces">
{workspaces.map(w => (
<Command.Item
key={w.id}
onSelect={() => switchTo(w.slug)}
>
<WorkspaceIcon workspace={w} />
{w.name}
{w.id === current.id && <Badge>Current</Badge>}
</Command.Item>
))}
</Command.Group>
</Command.List>
</Command.Dialog>
Keyboard shortcuts:
- cmd+K (mac) / ctrl+K (win) — open palette
- Arrow keys — navigate
- Enter — select
- Escape — close
Recent workspaces:
- Show 3-5 recent at top
- Boosts power-user speed
Search:
- Type to filter
- Fuzzy match
- Show keyboard shortcut on hover
Output:
1. cmd+K library choice (cmdk, kbar)
2. Component structure
3. Search behavior
4. Recent ordering
5. Mobile fallback (cmd+K not standard on touch)
The cmdk library by paco / pacocoursey is the de-facto standard in 2026 for command palettes. Used by Linear, Vercel, many others.
9. Logout — invalidate all sessions
When user logs out, kill auth across all workspaces.
Implement secure logout.
Logout flow:
- User clicks "Sign out"
- Server invalidates session token
- Client clears local state
- Redirect to login page
Session storage:
- HTTP-only cookie (recommended) — server invalidates server-side
- LocalStorage token — client clears; server denies
Multiple sessions:
- "Sign out all devices" option (revoke all sessions for user)
- Triggered on security events (password change, suspicious activity)
Workspace context cleanup:
- Last-active workspace cleared from cookie / localStorage
- On next login: prompt for workspace OR default to last (server-side)
Multi-tab:
- Logout in one tab → other tabs detect and redirect
- Use BroadcastChannel or storage event
Edge cases:
- Logout fails (network) → still clear client state; redirect
- Forced logout (admin revoked access) → user redirected with reason
Anti-patterns:
- Don't clear server session
- Don't redirect (user thinks they're still logged in)
- Don't kill multi-device when one device logs out (unless requested)
Audit:
- Log logout event (user_id, timestamp, IP)
- For incident response
Output:
1. Logout endpoint
2. Multi-device strategy
3. Multi-tab handling
4. Edge case handling
5. Audit logging
The multi-tab logout pattern: use the storage event or BroadcastChannel API to sync logout across tabs. Otherwise one tab logs out; others stay logged in (confusing).
10. Personal vs team workspace
Some products distinguish personal workspaces from team workspaces.
Decide personal vs team workspace.
Personal workspace:
- 1 user; private
- For their own use
- Examples: Notion personal, Linear personal
Team workspace:
- Multiple users
- Shared content
- Examples: Notion team, Linear team
Patterns:
Pattern 1: Always team (B2B)
- Even single-user workspace is "team of 1"
- Path to add members same as starting team
- Most B2B SaaS
Pattern 2: Personal + team distinct
- Personal is its own type; team is shared
- Different UX, different billing
- Example: Notion (personal Plan, team Plan)
Pattern 3: Personal becomes team
- Start as solo workspace
- Invite member → automatically becomes team
- Used by: many freemium products
Hybrid:
- Personal pages within team workspace
- "My private docs" inside shared workspace
- Notion's "Private pages" model
For B2B SaaS, recommendation:
- Always team-style (avoids personal/team distinction friction)
- "My pages" within team for private content if needed
Output:
1. Personal vs team decision
2. Migration path (if pattern 3)
3. Billing implications
4. UI differentiation
5. Default behavior on signup
The "always team" simplicity: signup → workspace created (team of 1) → can invite. No personal/team split. Easier to scale; fewer edge cases.
What Done Looks Like
A v1 workspace switcher for B2B SaaS in 2026:
- URL slug for workspace context (
/[workspace]/...) - Top-left dropdown switcher
- cmd+K command palette integration
- Last-active workspace remembered
- Multi-tenant data isolation server-side (RLS / ORM middleware)
- Invite flow with email + role
- Workspace settings page (general / members / billing / branding)
- Auto-join by domain (opt-in)
- Multi-tab logout sync
- Mobile-friendly switcher
Add later when product is mature:
- Subdomain per workspace (custom branding)
- SSO per workspace (Enterprise)
- Workspace transfer ownership
- Bulk member operations
- Audit log for workspace changes
- Workspace templates
- Per-workspace integrations
The mistake to avoid: client-side workspace_id without server validation. Easy to spoof; cross-tenant data leak.
The second mistake: slow switching (full page reload always). Modern users expect instant switch. Use client-side navigation.
The third mistake: no cmd+K integration. Power users in Linear / Notion / Vercel expect it.
See Also
- Multi-Tenancy — backend architecture (companion)
- Roles & Permissions — auth within workspace
- SSO & Enterprise Auth — workspace-scoped SSO
- Settings & Account Management Pages — workspace settings UX
- Audit Logs — log workspace changes
- Session Management Patterns — session per workspace
- Keyboard Shortcuts & Command Palette — cmd+K integration
- Quotas, Limits & Plan Enforcement — per-workspace limits
- Onboarding Tour Implementation — first-time-workspace
- Real-Time Collaboration — workspace-scoped collab
- VibeReference: Authentication — auth foundation
- VibeReference: Auth Providers — Clerk / Auth0 / etc.
- VibeReference: Better Auth — modern auth
- VibeReference: Components — UI primitives
- LaunchWeek: Vertical SaaS Positioning — multi-tenant for B2B