# This is NOT the Next.js you know This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices. # Agent Instructions ## Architecture Overview This is a **Next.js 16 + Convex (self-hosted) + Better Auth** SaaS template. - **Frontend**: Next.js 16 App Router, React 19, TypeScript 5, Tailwind CSS 4 - **Backend**: Convex self-hosted (Coolify) with Better Auth for authentication - **UI**: shadcn/ui (radix-nova style), @hugeicons/react icons - **i18n**: next-intl v4 with locale-based routing (`/en`, `/pl`) - **State**: React Server Components + Client Components hybrid. No global state library. - **Auth**: Better Auth with email/password, HIBP plugin, Convex adapter ### Key Files | File | Purpose | |------|---------| | `src/app/[locale]/layout.tsx` | Locale layout — validates locale, enables static rendering | | `src/app/layout.tsx` | Root layout — theme provider, i18n provider, dynamic `lang` | | `src/lib/auth-server.ts` | Server-side auth helpers (`isAuthenticated`, `getToken`) | | `src/lib/auth-client.ts` | Client-side auth client (`authClient.signIn.email(...)`) | | `src/i18n/routing.ts` | next-intl routing config (locales, defaultLocale, localePrefix) | | `src/i18n/request.ts` | Request config — reads locale from middleware, loads messages | | `src/proxy.ts` | next-intl middleware (Next.js 16 convention — was `middleware.ts`) | | `convex/auth.ts` | Better Auth configuration (plugins, password policy, HIBP) | | `convex/convex.config.ts` | Convex app definition | ### Directory Structure ``` src/ app/[locale]/ # All pages live under locale segment layout.tsx # Locale validation + setRequestLocale page.tsx # Home page dashboard/page.tsx # Protected dashboard (server page + isAuthenticated) settings/page.tsx # Protected settings (server page + isAuthenticated) sign-in/page.tsx # Auth form with callbackURL support sign-up/page.tsx # Auth form with callbackURL support api/auth/[...all]/ # Better Auth API route components/ ui/ # shadcn/ui primitives (button, card, field, etc.) auth/ # Auth-specific components settings/ # Settings-specific components core/ # App-wide components (ThemeChanger) lib/ auth-server.ts # Server auth helpers auth-client.ts # Client auth client routes.ts # Route constants utils.ts # cn() and utilities env.ts # Validated environment variables (Zod) i18n/ routing.ts # next-intl routing config request.ts # next-intl request config convex/ auth.ts # Better Auth setup auth.config.ts # Convex auth config provider convex.config.ts # Convex app definition http.ts # Convex HTTP actions betterAuth/ # Better Auth Convex component messages/ en.json # English translations pl.json # Polish translations ``` ## Conventions ### File Naming - **Components**: PascalCase (`PasswordChangeCard.tsx`, `AuthForm.tsx`) - **Pages**: `page.tsx` inside kebab-case directory (`sign-in/page.tsx`) - **Layouts**: `layout.tsx` - **Utilities**: camelCase (`auth-server.ts`, `utils.ts`) - **Constants**: `UPPER_SNAKE_CASE` for values, camelCase for files (`constants.ts`) ### Imports ```ts // Good import { Button } from '@/components/ui/button'; import { routes } from '@/lib/routes'; // Bad — never use @/src/components/ import { Card } from '@/src/components/ui/card'; ``` ### Auth Patterns #### Server Component (check auth) ```tsx import { isAuthenticated } from '@/lib/auth-server'; import { routes } from '@/lib/routes'; import { redirect } from 'next/navigation'; export default async function ProtectedPage() { const authenticated = await isAuthenticated(); if (!authenticated) { const searchParams = new URLSearchParams({ callbackURL: '/dashboard' }); redirect(`${routes.public.signIn}?${searchParams.toString()}`); } // ... render protected content } ``` #### Client Component (auth actions) ```tsx 'use client'; import { authClient } from '@/lib/auth-client'; async function handleSignIn(email: string, password: string) { const result = await authClient.signIn.email({ email, password, callbackURL: '/dashboard', }); if (result.error) { toast.error(result.error.message); } } ``` #### Password Change ```tsx const result = await authClient.changePassword({ currentPassword: values.currentPassword, newPassword: values.newPassword, revokeOtherSessions: true, }); ``` ### i18n Patterns #### Server Component ```tsx import { getTranslations } from 'next-intl/server'; export default async function Page() { const t = await getTranslations('Namespace'); return