Skip to main content

Overview

The TeamSwitcher component provides a select dropdown that allows users to switch between their teams. It includes team creation options, management links, and support for a “personal” or null team state. The component automatically displays team avatars and handles team switching logic.

Usage

import { TeamSwitcher } from '@stackframe/stack';

export default function Header() {
  return <TeamSwitcher />;
}

With Null/Personal Option

<TeamSwitcher allowNull nullLabel="Personal" />

Props

team
Team
The currently selected team object. Takes precedence over teamId.
teamId
string
The ID of the currently selected team. Used when team prop is not provided.
teams
Team[]
Array of available teams. If not provided, uses teams from the current user via useTeams() hook.
allowNull
boolean
default:"false"
Enables a “no team” or “personal” option in the dropdown for contexts where team selection is optional.
nullLabel
string
default:"No team"
Custom label for the null/personal option when allowNull is true.
triggerClassName
string
Additional CSS classes to apply to the select trigger button.
onChange
(team: Team | null) => Promise<void>
Callback function triggered when a team is selected. Receives the selected team or null (if allowNull is true).
mockUser
object
Mock user data for testing:
  • team: Currently selected mock team with id, displayName, and optional profileImageUrl
mockTeams
array
Array of mock teams for testing, each with id, displayName, and optional profileImageUrl.
mockProject
object
Mock project configuration:
  • config.clientTeamCreationEnabled: Enable/disable team creation button

Examples

Basic Team Switcher

import { TeamSwitcher } from '@stackframe/stack';

export default function TeamSelector() {
  return (
    <div className="p-4">
      <label className="block mb-2 text-sm font-medium">
        Select Team
      </label>
      <TeamSwitcher />
    </div>
  );
}

With Personal/No Team Option

import { TeamSwitcher } from '@stackframe/stack';

export default function Header() {
  return (
    <TeamSwitcher 
      allowNull 
      nullLabel="Personal Workspace"
    />
  );
}

With Custom Handler

import { TeamSwitcher } from '@stackframe/stack';
import { useRouter } from 'next/navigation';
import { Team } from '@stackframe/stack';

export default function ProjectSelector() {
  const router = useRouter();

  const handleTeamChange = async (team: Team | null) => {
    if (team) {
      // Navigate to team-specific dashboard
      router.push(`/teams/${team.id}/dashboard`);
    } else {
      // Navigate to personal dashboard
      router.push('/dashboard');
    }
  };

  return (
    <TeamSwitcher 
      allowNull
      onChange={handleTeamChange}
    />
  );
}

Controlled Component

import { TeamSwitcher } from '@stackframe/stack';
import { useUser, Team } from '@stackframe/stack';
import { useState } from 'react';

export default function ControlledTeamSwitcher() {
  const user = useUser();
  const teams = user?.useTeams() || [];
  const [selectedTeam, setSelectedTeam] = useState<Team | null>(null);

  return (
    <div>
      <TeamSwitcher
        team={selectedTeam}
        teams={teams}
        allowNull
        onChange={async (team) => {
          setSelectedTeam(team);
          console.log('Team changed to:', team?.displayName);
        }}
      />
      
      {selectedTeam && (
        <div className="mt-4">
          <h2>Current Team: {selectedTeam.displayName}</h2>
        </div>
      )}
    </div>
  );
}

With Custom Styling

import { TeamSwitcher } from '@stackframe/stack';

export default function StyledTeamSwitcher() {
  return (
    <TeamSwitcher 
      triggerClassName="w-full border-2 border-primary"
      allowNull
    />
  );
}

In Navigation Bar

import { TeamSwitcher, UserButton } from '@stackframe/stack';

export default function Navigation() {
  return (
    <nav className="border-b">
      <div className="container flex items-center justify-between py-4">
        <div className="flex items-center gap-4">
          <h1 className="text-xl font-bold">My App</h1>
          <TeamSwitcher />
        </div>
        <UserButton />
      </div>
    </nav>
  );
}

Features

Team Selection Groups

The dropdown organizes teams into logical groups:
  1. Current Team: The selected team with a settings button
  2. Personal/No Team (optional): When allowNull is enabled
  3. Other Teams: All other teams the user belongs to
  4. Create Team Button: When team creation is enabled

Team Icons

  • Displays team profile images when available
  • Falls back to team initials
  • Consistent icon sizing across all items
  • Special “personal” icon for null team option

Management Features

  • Quick access to team settings via gear icon
  • Deep links to team-specific settings pages
  • “Create a team” button when enabled in project config
  • Team creation redirects to account settings

Responsive Design

  • Maximum width of 256px (w-64) for optimal display
  • Truncated team names with ellipsis for long names
  • Loading skeleton while team data is being fetched
  • Smooth transitions and animations

Smart Sorting

  • Selected team automatically appears at the top
  • Remaining teams sorted by ID
  • Consistent ordering for better UX

Error Handling

  • Validates team selection
  • Throws assertion error if selected team not found
  • Proper async error handling with alerts
┌─────────────────────────────────┐
│ Current Team             ⚙️     │
│ ├─ Team Avatar                  │
│ └─ Team Name                    │
├─────────────────────────────────┤
│ Personal (if allowNull)         │
├─────────────────────────────────┤
│ Other Teams                     │
│ ├─ Team 1                       │
│ └─ Team 2                       │
├─────────────────────────────────┤
│ ➕ Create a team                │
└─────────────────────────────────┘

Integration

The TeamSwitcher integrates with:
  • User’s team membership data
  • Account settings interface
  • Team creation flow
  • Project configuration

Accessibility

  • Full keyboard navigation
  • Screen reader support
  • ARIA labels and roles
  • Focus management

Mock Mode

When using mock data:
  • Navigation actions are prevented
  • Team switching logs to console
  • Perfect for demos and testing

Build docs developers (and LLMs) love