Version 3 of the kit: - Radix UI replaced with Base UI (using the Shadcn UI patterns) - next-intl replaces react-i18next - enhanceAction deprecated; usage moved to next-safe-action - main layout now wrapped with [locale] path segment - Teams only mode - Layout updates - Zod v4 - Next.js 16.2 - Typescript 6 - All other dependencies updated - Removed deprecated Edge CSRF - Dynamic Github Action runner
215 lines
6.8 KiB
Plaintext
215 lines
6.8 KiB
Plaintext
---
|
|
status: "published"
|
|
label: "Paths Configuration"
|
|
title: "Paths Configuration in the Next.js Supabase SaaS Kit"
|
|
description: "Configure route paths for authentication, personal accounts, and team accounts in the Next.js Supabase SaaS Kit. Centralized path management for consistent navigation."
|
|
order: 3
|
|
---
|
|
|
|
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.
|
|
|
|
{% alert type="default" title="Why Centralize Paths" %}
|
|
Centralizing paths prevents typos, makes refactoring easier, and ensures consistency across navigation, redirects, and links throughout your application.
|
|
{% /alert %}
|
|
|
|
## 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 |
|
|
|
|
### Personal Account Paths
|
|
|
|
| Path Key | Default Value | Description |
|
|
|----------|---------------|-------------|
|
|
| `app.home` | `/home` | Personal dashboard |
|
|
| `app.personalAccountSettings` | `/home/settings` | Profile settings |
|
|
| `app.personalAccountBilling` | `/home/billing` | Billing management |
|
|
| `app.personalAccountBillingReturn` | `/home/billing/return` | Billing portal return |
|
|
|
|
### Team Account Paths
|
|
|
|
| Path Key | Default Value | Description |
|
|
|----------|---------------|-------------|
|
|
| `app.accountHome` | `/home/[account]` | Team dashboard |
|
|
| `app.accountSettings` | `/home/[account]/settings` | Team settings |
|
|
| `app.accountBilling` | `/home/[account]/billing` | Team billing |
|
|
| `app.accountMembers` | `/home/[account]/members` | Team members |
|
|
| `app.accountBillingReturn` | `/home/[account]/billing/return` | Team billing return |
|
|
| `app.joinTeam` | `/join` | Team invitation acceptance |
|
|
|
|
## Configuration File
|
|
|
|
```typescript
|
|
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),
|
|
personalAccountSettings: z.string().min(1),
|
|
personalAccountBilling: z.string().min(1),
|
|
personalAccountBillingReturn: z.string().min(1),
|
|
accountHome: z.string().min(1),
|
|
accountSettings: z.string().min(1),
|
|
accountBilling: z.string().min(1),
|
|
accountMembers: z.string().min(1),
|
|
accountBillingReturn: z.string().min(1),
|
|
joinTeam: 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: '/home',
|
|
personalAccountSettings: '/home/settings',
|
|
personalAccountBilling: '/home/billing',
|
|
personalAccountBillingReturn: '/home/billing/return',
|
|
accountHome: '/home/[account]',
|
|
accountSettings: `/home/[account]/settings`,
|
|
accountBilling: `/home/[account]/billing`,
|
|
accountMembers: `/home/[account]/members`,
|
|
accountBillingReturn: `/home/[account]/billing/return`,
|
|
joinTeam: '/join',
|
|
},
|
|
});
|
|
|
|
export default pathsConfig;
|
|
```
|
|
|
|
## Using Paths in Code
|
|
|
|
### In Server Components and Actions
|
|
|
|
```typescript
|
|
import pathsConfig from '~/config/paths.config';
|
|
import { redirect } from 'next/navigation';
|
|
|
|
// Redirect to sign in
|
|
redirect(pathsConfig.auth.signIn);
|
|
|
|
// Redirect to team dashboard
|
|
const teamSlug = 'acme-corp';
|
|
redirect(pathsConfig.app.accountHome.replace('[account]', teamSlug));
|
|
```
|
|
|
|
### In Client Components
|
|
|
|
```tsx
|
|
import pathsConfig from '~/config/paths.config';
|
|
import Link from 'next/link';
|
|
|
|
function Navigation() {
|
|
return (
|
|
<nav>
|
|
<Link href={pathsConfig.app.home}>Dashboard</Link>
|
|
<Link href={pathsConfig.app.personalAccountSettings}>Settings</Link>
|
|
</nav>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Dynamic Team Paths
|
|
|
|
Team account paths contain `[account]` as a placeholder. Replace it with the actual team slug:
|
|
|
|
```typescript
|
|
import pathsConfig from '~/config/paths.config';
|
|
|
|
function getTeamPaths(teamSlug: string) {
|
|
return {
|
|
dashboard: pathsConfig.app.accountHome.replace('[account]', teamSlug),
|
|
settings: pathsConfig.app.accountSettings.replace('[account]', teamSlug),
|
|
billing: pathsConfig.app.accountBilling.replace('[account]', teamSlug),
|
|
members: pathsConfig.app.accountMembers.replace('[account]', teamSlug),
|
|
};
|
|
}
|
|
|
|
// Usage
|
|
const paths = getTeamPaths('acme-corp');
|
|
// paths.dashboard = '/home/acme-corp'
|
|
// paths.settings = '/home/acme-corp/settings'
|
|
```
|
|
|
|
## Adding Custom Paths
|
|
|
|
Extend the schema when adding new routes to your application:
|
|
|
|
```typescript
|
|
const PathsSchema = z.object({
|
|
auth: z.object({
|
|
// ... existing auth paths
|
|
}),
|
|
app: z.object({
|
|
// ... existing app paths
|
|
// Add your custom paths
|
|
projects: z.string().min(1),
|
|
projectDetail: z.string().min(1),
|
|
}),
|
|
});
|
|
|
|
const pathsConfig = PathsSchema.parse({
|
|
auth: {
|
|
// ... existing values
|
|
},
|
|
app: {
|
|
// ... existing values
|
|
projects: '/home/[account]/projects',
|
|
projectDetail: '/home/[account]/projects/[projectId]',
|
|
},
|
|
});
|
|
```
|
|
|
|
Then use the new paths:
|
|
|
|
```typescript
|
|
const projectsPath = pathsConfig.app.projects
|
|
.replace('[account]', teamSlug);
|
|
|
|
const projectDetailPath = pathsConfig.app.projectDetail
|
|
.replace('[account]', teamSlug)
|
|
.replace('[projectId]', projectId);
|
|
```
|
|
|
|
## Path Conventions
|
|
|
|
Follow these conventions when adding paths:
|
|
|
|
1. **Use lowercase with hyphens**: `/home/my-projects` not `/home/myProjects`
|
|
2. **Use brackets for dynamic segments**: `[account]`, `[projectId]`
|
|
3. **Keep paths shallow**: Avoid deeply nested routes when possible
|
|
4. **Group related paths**: Put team-related paths under `app.account*`
|
|
|
|
## Common Pitfalls
|
|
|
|
1. **Forgetting to replace dynamic segments**: Always replace `[account]` with the actual slug before using team paths in redirects or links.
|
|
2. **Hardcoding paths**: Don't use string literals like `'/home/settings'`. Always import from `pathsConfig` for consistency.
|
|
3. **Missing trailing slash consistency**: The kit doesn't use trailing slashes. Keep this consistent in your custom paths.
|
|
|
|
## Related Topics
|
|
|
|
- [Navigation Configuration](/docs/next-supabase-turbo/configuration/personal-account-sidebar-configuration) - Configure sidebar navigation
|
|
- [App Router Structure](/docs/next-supabase-turbo/installation/navigating-codebase) - Understand the route organization
|