Paths Configuration in the Tanstack Start Supabase SaaS Kit
Configure route paths for authentication, personal accounts, and team accounts in the Tanstack Start Supabase SaaS Kit. Centralized path management for consistent navigation.
The paths configuration at apps/web/config/paths.config.ts centralizes all route definitions. Instead of scattering magic strings throughout your codebase, reference paths from this single configuration file.
Why Centralize Paths
Centralizing paths prevents typos, makes refactoring easier, and ensures consistency across navigation, redirects, and links throughout your application.
Slug-free, DB-backed active account
The kit does not encode the current workspace in the URL. Each user has an active account (personal or team) persisted in the database (public.user_active_account), resolved server-side by public.active_account_id(). Because of this, every authenticated path is static — there is no [account] slug to substitute. Switching workspace mutates the active-account pointer and re-runs the loaders; the URL never changes.
Default Paths
Authentication Paths
| Path Key | Default Value | Description |
|---|---|---|
auth.signIn | /auth/sign-in | Sign in page |
auth.signUp | /auth/sign-up | Sign up page |
auth.verifyMfa | /auth/verify | MFA verification |
auth.callback | /auth/callback | OAuth callback handler |
auth.passwordReset | /auth/password-reset | Password reset request |
auth.passwordUpdate | /update-password | Password update completion |
App Paths
These serve both personal and team accounts — the active account determines what each page renders.
| Path Key | Default Value | Description |
|---|---|---|
app.home | /dashboard | Post-login dashboard for the active account |
app.settings | /settings | Settings (profile for a personal account, team settings for a team) |
app.settingsMembers | /settings/members | Team members (redirects to /settings for a personal account) |
app.settingsBilling | /settings/billing | Billing management |
app.settingsBillingReturn | /settings/billing/return | Billing portal / checkout return |
app.createTeam | /create-team | Create a team account |
app.joinTeam | /join | Team invitation acceptance |
Configuration File
import * as z from 'zod';const PathsSchema = z.object({ auth: z.object({ signIn: z.string().min(1), signUp: z.string().min(1), verifyMfa: z.string().min(1), callback: z.string().min(1), passwordReset: z.string().min(1), passwordUpdate: z.string().min(1), }), app: z.object({ home: z.string().min(1), joinTeam: z.string().min(1), createTeam: z.string().min(1), settings: z.string().min(1), settingsMembers: z.string().min(1), settingsBilling: z.string().min(1), settingsBillingReturn: z.string().min(1), }),});const pathsConfig = PathsSchema.parse({ auth: { signIn: '/auth/sign-in', signUp: '/auth/sign-up', verifyMfa: '/auth/verify', callback: '/auth/callback', passwordReset: '/auth/password-reset', passwordUpdate: '/update-password', }, app: { home: '/dashboard', joinTeam: '/join', createTeam: '/create-team', settings: '/settings', settingsMembers: '/settings/members', settingsBilling: '/settings/billing', settingsBillingReturn: '/settings/billing/return', },});export default pathsConfig;Using Paths in Code
In Server Functions and Loaders
import { redirect } from '@tanstack/react-router';import pathsConfig from '#/config/paths.config.ts';// Redirect to sign inthrow redirect({ href: pathsConfig.auth.signIn });// Redirect to the dashboard (renders whichever account is active)throw redirect({ href: pathsConfig.app.home });In Client Components
import { Link } from '@tanstack/react-router';import pathsConfig from '#/config/paths.config.ts';function Navigation() { return ( <nav> <Link to={pathsConfig.app.home}>Dashboard</Link> <Link to={pathsConfig.app.settings}>Settings</Link> </nav> );}Reading and switching the active account
Read the active account from the workspace context; switch it with the setActiveAccountFunction server function.
import { useWorkspace } from '#/components/workspace-context.tsx';function Header() { const { account } = useWorkspace(); return account.is_personal_account ? <PersonalHeader /> : <TeamHeader />;}Adding Custom Paths
Extend the schema when adding new routes to your application. New authenticated pages are static too — scope them under /dashboard or /settings and let the active account decide what they show:
const PathsSchema = z.object({ auth: z.object({ // ... existing auth paths }), app: z.object({ // ... existing app paths projects: z.string().min(1), }),});const pathsConfig = PathsSchema.parse({ auth: { // ... existing values }, app: { // ... existing values projects: '/dashboard/projects', },});Path Conventions
Follow these conventions when adding paths:
- Use lowercase with hyphens:
/dashboard/my-projectsnot/dashboard/myProjects - Keep paths static: the active account is resolved from the database, not the URL — never encode an account id or slug in a path
- Keep paths shallow: avoid deeply nested routes when possible
- Group related paths: put settings-related paths under
app.settings*
Common Pitfalls
- Reintroducing a slug: don't add account ids/slugs to URLs. Resolve the active account server-side (
active_account_id()) or fromuseWorkspace(). - Hardcoding paths: don't use string literals like
'/settings'. Always import frompathsConfigfor consistency. - Missing trailing slash consistency: the kit doesn't use trailing slashes. Keep this consistent in your custom paths.
Related Topics
- Navigation Configuration - Configure sidebar navigation