Team Account Navigation Configuration in the Next.js Supabase SaaS Kit

Configure the team account sidebar navigation, layout style, and menu structure in the Next.js Supabase SaaS Kit for B2B team workspaces.

The team account navigation at apps/web/config/team-account-navigation.config.tsx defines the sidebar menu for team workspaces. This configuration differs from personal navigation because routes include the team slug as a dynamic segment.

Layout Options

VariableOptionsDefaultDescription
NEXT_PUBLIC_TEAM_NAVIGATION_STYLEsidebar, headersidebarNavigation layout style
NEXT_PUBLIC_TEAM_SIDEBAR_COLLAPSEDtrue, falsefalseStart with collapsed sidebar
NEXT_PUBLIC_SIDEBAR_COLLAPSIBLE_STYLEoffcanvas, icon, noneiconHow sidebar collapses
NEXT_PUBLIC_TEAM_NAVIGATION_STYLE=sidebar

Header Style

NEXT_PUBLIC_TEAM_NAVIGATION_STYLE=header

Default Configuration

The kit ships with these team routes:

import { CreditCard, LayoutDashboard, Settings, Users } from 'lucide-react';
import { NavigationConfigSchema } from '@kit/ui/navigation-schema';
import featureFlagsConfig from '~/config/feature-flags.config';
import pathsConfig from '~/config/paths.config';
const iconClasses = 'w-4';
const getRoutes = (account: string) => [
{
label: 'common:routes.application',
children: [
{
label: 'common:routes.dashboard',
path: pathsConfig.app.accountHome.replace('[account]', account),
Icon: <LayoutDashboard className={iconClasses} />,
end: true,
},
],
},
{
label: 'common:routes.settings',
collapsible: false,
children: [
{
label: 'common:routes.settings',
path: createPath(pathsConfig.app.accountSettings, account),
Icon: <Settings className={iconClasses} />,
},
{
label: 'common:routes.members',
path: createPath(pathsConfig.app.accountMembers, account),
Icon: <Users className={iconClasses} />,
},
featureFlagsConfig.enableTeamAccountBilling
? {
label: 'common:routes.billing',
path: createPath(pathsConfig.app.accountBilling, account),
Icon: <CreditCard className={iconClasses} />,
}
: undefined,
].filter(Boolean),
},
];
export function getTeamAccountSidebarConfig(account: string) {
return NavigationConfigSchema.parse({
routes: getRoutes(account),
style: process.env.NEXT_PUBLIC_TEAM_NAVIGATION_STYLE,
sidebarCollapsed: process.env.NEXT_PUBLIC_TEAM_SIDEBAR_COLLAPSED,
sidebarCollapsedStyle: process.env.NEXT_PUBLIC_SIDEBAR_COLLAPSIBLE_STYLE,
});
}
function createPath(path: string, account: string) {
return path.replace('[account]', account);
}

Key Differences from Personal Navigation

AspectPersonal NavigationTeam Navigation
ExportpersonalAccountNavigationConfig (object)getTeamAccountSidebarConfig(account) (function)
PathsStatic (e.g., /home/settings)Dynamic (e.g., /home/acme-corp/settings)
ContextSingle user workspaceTeam-specific workspace

Adding Custom Routes

Simple Route

Add a route to an existing section. Use the createPath helper to inject the team slug:

const getRoutes = (account: string) => [
{
label: 'common:routes.application',
children: [
{
label: 'common:routes.dashboard',
path: createPath(pathsConfig.app.accountHome, account),
Icon: <LayoutDashboard className={iconClasses} />,
end: true,
},
{
label: 'Projects',
path: createPath('/home/[account]/projects', account),
Icon: <Folder className={iconClasses} />,
},
],
},
// ... rest of routes
];

New Section

Add a new section for team-specific features:

const getRoutes = (account: string) => [
// ... existing sections
{
label: 'Workspace',
children: [
{
label: 'Projects',
path: createPath('/home/[account]/projects', account),
Icon: <Folder className={iconClasses} />,
},
{
label: 'Documents',
path: createPath('/home/[account]/documents', account),
Icon: <FileText className={iconClasses} />,
},
{
label: 'Integrations',
path: createPath('/home/[account]/integrations', account),
Icon: <Plug className={iconClasses} />,
},
],
},
];

Conditional Routes

Show routes based on feature flags or team permissions:

const getRoutes = (account: string) => [
{
label: 'common:routes.settings',
collapsible: false,
children: [
{
label: 'common:routes.settings',
path: createPath(pathsConfig.app.accountSettings, account),
Icon: <Settings className={iconClasses} />,
},
{
label: 'common:routes.members',
path: createPath(pathsConfig.app.accountMembers, account),
Icon: <Users className={iconClasses} />,
},
// Only show billing if enabled
featureFlagsConfig.enableTeamAccountBilling
? {
label: 'common:routes.billing',
path: createPath(pathsConfig.app.accountBilling, account),
Icon: <CreditCard className={iconClasses} />,
}
: undefined,
].filter(Boolean),
},
];

Route Properties

PropertyTypeRequiredDescription
labelstringYesDisplay text (supports i18n keys)
pathstringYesRoute path with team slug
IconReactNodeNoLucide icon component
endbooleanNoExact path matching (for index routes)
childrenRoute[]NoNested routes for sub-menus
collapsiblebooleanNoWhether section can collapse

Using the createPath Helper

Always use the createPath helper to replace [account] with the team slug:

function createPath(path: string, account: string) {
return path.replace('[account]', account);
}
// Usage
const settingsPath = createPath('/home/[account]/settings', 'acme-corp');
// Result: '/home/acme-corp/settings'

For paths defined in pathsConfig, use the same pattern:

createPath(pathsConfig.app.accountSettings, account)
// Converts '/home/[account]/settings' to '/home/acme-corp/settings'

Non-Collapsible Sections

Set collapsible: false to keep a section always expanded:

{
label: 'common:routes.settings',
collapsible: false, // Always expanded
children: [
// ... routes
],
}

Best Practices

  1. Always use createPath: Never hardcode team slugs. Always use the helper function.
  2. Keep paths in pathsConfig: When adding new routes, add them to paths.config.ts first.
  3. Filter undefined routes: When using conditional routes, always add .filter(Boolean).
  4. Use the end property: Add end: true to index routes to prevent matching nested paths.
  5. Consider mobile: Test navigation on mobile devices. Complex nested menus can be hard to navigate.

Common Pitfalls

  1. Forgetting to replace [account]: If paths show [account] literally in the URL, you forgot to use createPath.
  2. Not exporting as function: Team navigation must be a function that accepts the account slug, not a static object.
  3. Mixing personal and team routes: Team routes should use /home/[account]/..., personal routes use /home/....
  4. Hardcoding team slugs: Never hardcode a specific team slug. Always use the account parameter.