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.
- Next.js 16.2.6 (App Router), React 19.2.4, TypeScript 5, Tailwind v4
- Prisma 7.8 with driver adapter (
@prisma/adapter-mariadb), MySQL - better-auth 1.6 with Prisma adapter
- shadcn (style
radix-luma, baseColormist, iconLibraryremixicon) - zustand 5 for client cart state (persisted to
localStorage)
npm run dev/build/start/lint— only these are defined- No
testortypecheckscript. Don't invent one; ask before adding
@/* → ./src/* (set in tsconfig.json and mirrored in components.json)
- Custom generator output:
prisma/schema.prismawrites to../generated/prisma(notnode_modules/.prisma/client) - Client import must use the custom path:
import { PrismaClient } from "../../generated/prisma/client"(seesrc/lib/prisma.ts) - The client is instantiated via a driver adapter, not the default engine:
new PrismaClient({ adapter: new PrismaMariaDb(process.env.DATABASE_URL!) }) prisma.config.tsloads env viaimport "dotenv/config"— required fornpx prismacommandsgenerated/prismais gitignored. Runnpx prisma generateafternpm installand after any schema change- Better-auth tables map to lowercase DB names via
@@map:user,session,account,verification— don't rename or Prisma + better-auth will desync
cacheComponents: trueis set innext.config.ts— server components are dynamic by default; caching must be opted in- Dynamic route signal: server components that need fresh data use
import { connection } from "next/server"; await connection();(seesrc/app/(front)/product/page.tsx) - Prisma
Decimalcolumns are not serializable to client components — convert tonumberon the server before passing down - Catch-all auth handler:
src/app/api/auth/[...all]/route.tsre-exportstoNextJsHandler(auth)frombetter-auth/next-js
- shadcn primitives live in
src/components/ui/. Add new ones via theshadcnCLI, not by hand - Use
cn(...)from@/lib/utilsfor className merging (clsx + tailwind-merge) - Feature components:
src/components/*.tsx(shared),src/app/(front)/components/*.tsx(front-route-only) - Default language is Thai (
<html lang="th">); UI strings and metadata are Thai - Form pattern:
react-hook-formviaController+zodresolver, using shadcn'sField/FieldGroup/FieldLabel/FieldErrorcomponents (seesrc/app/(auth)/login/page.tsx) - Two icon libraries:
@remixicon/react(shadcn default percomponents.json) andlucide-react(used in navbar) — prefer remixicon for shadcn-generated code @tailwindcss/postcssplugin (Tailwind v4), not the classictailwindcssPostCSS plugin
- Cart store:
useCartStorefromsrc/lib/cart-store.ts, persisted under localStorage keyskill-cart - Auth state on the server:
await auth.api.getSession({ headers: await headers() })— seesrc/components/navbar.tsx
Dockerfile copies .next/standalone and .next/static, but next.config.ts does not set output: "standalone". The container build will fail or skip standalone output until that is added. If you touch one, touch the other.
.env.exampleis checked in and contains a real-lookingDATABASE_URLpassword. Treat that value as compromised — do not reuse it anywhere- Required env vars:
DATABASE_URL,BETTER_AUTH_SECRET,BETTER_AUTH_URL(see.env.example)