Concepts

Selecting a Team

Switch between multiple teams of a user

A user can be a member of multiple teams, so most websites using teams will need a way to select a “current team” that the user is working on. There are two primary methods to accomplish this:

  • Deep Link: Each team has a unique URL, for example, your-website.com/team/<team-id>. When a team is selected, it redirects to a page with that team’s URL.
  • Current Team: When a user selects a team, the app stores the team as a global “current team” state. In this way, the URL of the current team might be something like your-website.com/current-team, and the URL won’t change after switching teams.

The deep link method is generally recommended because it avoids some common issues associated with the current team method. If two users share a link while using deep link URLs, the receiving user will always be directed to the correct team’s information based on the link.

Current Team Method

While the current team method can be simpler to implement, it has a downside. If a user shares a link, the recipient might see information about the wrong team (if their “current team” is set differently). This method can also cause problems when a user has multiple browser tabs open with different teams.

Selected Team Switcher

To facilitate team selection, Stack provides a component that looks like this:

TeamSwitcher

You can import and use the SelectedTeamSwitcher component for the “current team” method. It updates the selectedTeam when a user selects a team:

1import { SelectedTeamSwitcher } from "@stackframe/stack";
2
3export function MyPage() {
4 return (
5 <div>
6 <SelectedTeamSwitcher/>
7 </div>
8 );
9}

To combine the switcher with the deep link method, you can pass in urlMap and selectedTeam. The urlMap is a function to generate a URL based on the team information, and selectedTeam is the team that the user is currently working on. This lets you implement “deep link” + “most recent team”. The component will update the user.selectedTeam with the selectedTeam prop:

1<SelectedTeamSwitcher
2 urlMap={team => `/team/${team.id}`}
3 selectedTeam={team}
4/>

To implement the “deep link” + “default team” method, where you update the selectedTeam only when the user clicks “set to default team” or similar, pass noUpdateSelectedTeam:

1<SelectedTeamSwitcher
2 urlMap={team => `/team/${team.id}`}
3 selectedTeam={team}
4 noUpdateSelectedTeam
5/>

First, create a page at /app/team/[teamId]/page.tsx to display information about a specific team:

/app/team/[teamId]/page.tsx
1"use client";
2
3import { useUser, SelectedTeamSwitcher } from "@stackframe/stack";
4
5export default function TeamPage({ params }: { params: { teamId: string } }) {
6 const user = useUser({ or: 'redirect' });
7 const team = user.useTeam(params.teamId);
8
9 if (!team) {
10 return <div>Team not found</div>;
11 }
12
13 return (
14 <div>
15 <SelectedTeamSwitcher
16 urlMap={team => `/team/${team.id}`}
17 selectedTeam={team}
18 />
19
20 <p>Team Name: {team.displayName}</p>
21 <p>You are a member of this team.</p>
22 </div>
23 );
24}

Next, create a page to display all teams at /app/team/page.tsx:

/app/team/page.tsx
1"use client";
2
3import { useRouter } from "next/navigation";
4import { useUser } from "@stackframe/stack";
5
6export default function TeamsPage() {
7 const user = useUser({ or: 'redirect' });
8 const teams = user.useTeams();
9 const router = useRouter();
10 const selectedTeam = user.selectedTeam;
11
12 return (
13 <div>
14 {selectedTeam &&
15 <button onClick={() => router.push(`/team/${selectedTeam.id}`)}>
16 Most recent team
17 </button>}
18
19 <h2>All Teams</h2>
20 {teams.map(team => (
21 <button key={team.id} onClick={() => router.push(`/team/${team.id}`)}>
22 Open {team.displayName}
23 </button>
24 ))}
25 </div>
26 );
27}

Now, if you navigate to http://localhost:3000/team, you should be able to see and interact with the teams.