* chore: update project dependencies and documentation for Next.js 16 - Upgraded Next.js from version 15 to 16 across various documentation files and components. - Updated references to Next.js 16 in AGENTS.md and CLAUDE.md for consistency. - Incremented application version to 2.21.0 in package.json. - Refactored identity setup components to improve user experience and added confirmation dialogs for authentication methods. - Enhanced invitation flow with new logic for handling user redirection and token generation. * refactor: streamline invitation flow in e2e tests - Simplified the invitation flow test by using a predefined email instead of generating a random one. - Removed unnecessary steps such as clearing cookies and reloading the page before user sign-up. - Enhanced clarity by eliminating commented-out code related to identity verification and user membership checks. * refactor: improve code readability in IdentitiesPage and UpdatePasswordForm components - Enhanced formatting of JSX elements in IdentitiesPage and UpdatePasswordForm for better readability. - Adjusted indentation and line breaks to maintain consistent coding style across components. * refactor: enhance LinkAccountsList component with user redirection logic - Updated the LinkAccountsList component to include a redirectToPath option in the useLinkIdentityWithProvider hook for improved user experience. - Removed redundant user hook declaration to streamline the code structure. * refactor: update account setup logic in JoinTeamAccountPage - Introduced a check for email-only authentication support to streamline account setup requirements. - Adjusted the conditions for determining if a new account should set up additional authentication methods, enhancing user experience for new users.
8.5 KiB
Web Application Instructions
This file contains instructions specific to the main Next.js web application.
Application Structure
Route Organization
app/
├── (marketing)/ # Public pages (landing, blog, docs)
├── (auth)/ # Authentication pages
├── home/
│ ├── (user)/ # Personal account context
│ └── [account]/ # Team account context ([account] = team slug)
├── admin/ # Super admin section
└── api/ # API routes
Key Examples:
- Marketing layout:
app/(marketing)/layout.tsx - Personal dashboard:
app/home/(user)/page.tsx - Team workspace:
app/home/[account]/page.tsx - Admin section:
app/admin/page.tsx
Component Organization
- Route-specific: Use
_components/directories - Route utilities: Use
_lib/for client,_lib/server/for server-side - Global components: Root-level directories
Example:
- Team components:
app/home/[account]/_components/ - Team server utils:
app/home/[account]/_lib/server/ - Marketing components:
app/(marketing)/_components/
The [account] parameter is the accounts.slug property, not the ID
React Server Components - Async Pattern
CRITICAL: In Next.js 16, always await params directly in async server components:
// ❌ WRONG - Don't use React.use() in async functions
async function Page({ params }: Props) {
const { account } = use(params);
}
// ✅ CORRECT - await params directly in Next.js 16
async function Page({ params }: Props) {
const { account } = await params; // ✅ Server component pattern
}
// ✅ CORRECT - "use" in non-async functions in Next.js 16
function Page({ params }: Props) {
const { account } = use(params); // ✅ Server component pattern
}
Data Fetching Strategy
Quick Decision Framework:
- Server Components: Default choice for initial data loading
- Client Components: For interactive features requiring hooks or real-time updates
- Admin Client: Only for bypassing RLS (rare cases - requires manual auth/authorization)
Server Components (Preferred) ✅
import { getSupabaseServerClient } from '@kit/supabase/server-client';
async function NotesPage() {
const client = getSupabaseServerClient();
const { data, error } = await client.from('notes').select('*');
if (error) return <ErrorMessage error={error} />;
return <NotesList notes={data} />;
}
Key Insight: Server Components automatically inherit RLS protection - no additional authorization checks needed!
Client Components (Interactive) 🖱️
'use client';
import { useSupabase } from '@kit/supabase/hooks/use-supabase';
import { useQuery } from '@tanstack/react-query';
function InteractiveNotes() {
const supabase = useSupabase();
const { data, isLoading } = useQuery({
queryKey: ['notes'],
queryFn: () => supabase.from('notes').select('*')
});
if (isLoading) return <Spinner />;
return <NotesList notes={data} />;
}
Performance Optimization - Parallel Data Fetching 🚀
Sequential (Slow) Pattern ❌
async function SlowDashboard() {
const userData = await loadUserData();
const notifications = await loadNotifications();
const metrics = await loadMetrics();
// Total time: sum of all requests
}
Parallel (Optimized) Pattern ✅
async function FastDashboard() {
// Execute all requests simultaneously
const [userData, notifications, metrics] = await Promise.all([
loadUserData(),
loadNotifications(),
loadMetrics()
]);
// Total time: longest single request
return <Dashboard user={userData} notifications={notifications} metrics={metrics} />;
}
Performance Impact: Parallel fetching can reduce page load time by 60-80% for multi-data pages!
Authorization Patterns - Critical Understanding 🔐
RLS-Protected Data Fetching (Standard) ✅
async function getUserNotes(userId: string) {
const client = getSupabaseServerClient();
// RLS automatically ensures user can only access their own notes
// NO additional authorization checks needed!
const { data } = await client.from('notes').select('*').eq('user_id', userId); // RLS validates this automatically
return data;
}
Admin Client Usage (Dangerous - Rare Cases Only) ⚠️
async function adminGetUserNotes(userId: string) {
const adminClient = getSupabaseServerAdminClient();
// CRITICAL: Manual authorization required - bypasses RLS!
const currentUser = await getCurrentUser();
if (!(await isSuperAdmin(currentUser))) {
throw new Error('Unauthorized: Admin access required');
}
// Additional validation: ensure current admin isn't targeting themselves
if (currentUser.id === userId) {
throw new Error('Cannot perform admin action on own account');
}
// Now safe to proceed with admin privileges
const { data } = await adminClient
.from('notes')
.select('*')
.eq('user_id', userId);
return data;
}
Rule of thumb: If using standard Supabase client, trust RLS. If using admin client, validate everything manually.
Internationalization
Always use Trans component from @kit/ui/trans:
import { Trans } from '@kit/ui/trans';
<Trans
i18nKey="user:welcomeMessage"
values={{ name: user.name }}
/>
// With HTML elements
<Trans
i18nKey="terms:agreement"
components={{
TermsLink: <a href="/terms" className="underline" />,
}}
/>
Adding New Languages
- Add language code to
lib/i18n/i18n.settings.ts - Create translation files in
public/locales/[new-language]/ - Copy structure from English files
Adding new namespaces
- Translation files:
public/locales/<locale>/<namespace>.json - Add namespace to
defaultI18nNamespacesinapps/web/lib/i18n/i18n.settings.ts
Workspace Contexts 🏢
Personal Account Context (app/home/(user))
import { useUserWorkspace } from '@kit/accounts/hooks/use-user-workspace';
function PersonalComponent() {
const { user, account } = useUserWorkspace();
// Personal account data
}
Context provider: @packages/features/accounts/src/components/user-workspace-context-provider.tsx
Team Account Context (app/home/[account])
import { useTeamAccountWorkspace } from '@kit/team-accounts/hooks/use-team-account-workspace';
function TeamComponent() {
const { account, user, accounts } = useTeamAccountWorkspace();
// Team account data with permissions
}
Context provider: @packages/features/team-accounts/src/components/team-account-workspace-context-provider.tsx
Key Configuration Files
- Feature flags:
config/feature-flags.config.ts - i18n settings:
lib/i18n/i18n.settings.ts - Supabase config:
supabase/config.toml - Middleware:
middleware.ts
Route Handlers (API Routes)
Use enhanceRouteHandler from @packages/next/src/routes/index.ts:
import { enhanceRouteHandler } from '@kit/next/routes';
export const POST = enhanceRouteHandler(
async function ({ body, user, request }) {
// body is validated, user available if auth: true
return NextResponse.json({ success: true });
},
{
auth: true,
schema: ZodSchema,
},
);
Navigation Menu Configuration 🗺️
Adding Sidebar Menu Items
Config Files:
- Personal:
config/personal-account-navigation.config.tsx - Team:
config/team-account-navigation.config.tsx
Add to Personal Navigation:
{
label: 'common:routes.yourFeature',
path: pathsConfig.app.yourFeaturePath,
Icon: <YourIcon className="w-4" />,
end: true,
},
Add to Team Navigation:
{
label: 'common:routes.yourTeamFeature',
path: createPath(pathsConfig.app.yourTeamFeaturePath, account),
Icon: <YourIcon className="w-4" />,
},
Add Paths:
// config/paths.config.ts
app: {
yourFeaturePath: '/home/your-feature',
yourTeamFeaturePath: '/home/[account]/your-feature',
}
Add Translations:
// public/locales/en/common.json
"routes": {
"yourFeature": "Your Feature"
}
Security Guidelines 🛡️
Authentication & Authorization
- Authentication already enforced by middleware
- Authorization handled by RLS at database level (in most cases)
- Avoid defensive code - use RLS instead
- When using the Supabase admin client, must enforce both authentication and authorization
Passing data to the client
- Never pass sensitive data to Client Components
- Never expose server environment variables to client (unless prefixed with NEXT_PUBLIC)
- Always validate user input