Next.js Supabase V3 (#463)

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
This commit is contained in:
Giancarlo Buomprisco
2026-03-24 13:40:38 +08:00
committed by GitHub
parent 4912e402a3
commit 7ebff31475
840 changed files with 71395 additions and 20095 deletions

View File

@@ -0,0 +1,214 @@
---
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