Everything below is already installed and wired. Check here before writing code that needs a capability, and never install a package that duplicates one of these.
| Need | Use | Not |
|---|---|---|
| Forms | react-hook-form + zod + @hookform/resolvers | formik, custom form state |
| Validation | zod (v4) | joi, yup, hand-rolled checks |
| UI components | shadcn/ui via npx shadcn add |
MUI, Chakra, custom modals |
| Icons | lucide-react | emoji in UI, other icon packs |
| Client state | zustand (v5) | Redux, context spaghetti |
| Server state | @tanstack/react-query (v5) | useEffect-fetch patterns |
| HTTP | axios | node-fetch, superagent |
| Auth | next-auth v5 (Auth.js) + @auth/prisma-adapter | custom sessions, JWT rolls |
| Database | prisma (v7) + @prisma/client | raw SQL, other ORMs |
| Dates | date-fns (v4) | moment, dayjs |
| Animation | motion (import { motion } from "motion/react") |
framer-motion (old name), GSAP |
| Toasts | sonner (v2) | react-hot-toast, custom toasts |
| File upload UI | react-dropzone | hand-rolled drag/drop |
| Dark mode | next-themes (.dark class strategy) |
media-query-only hacks |
| IDs | nanoid | uuid, Math.random |
| Env vars | @t3-oss/env-nextjs | raw process.env reads |
| SEO | Next.js Metadata API (built in) | next-seo or any package |
| Styling | tailwindcss v4 + tokens in globals.css | CSS modules, styled-components |
| Class merging | clsx + tailwind-merge via cn() |
string concatenation |
| Testing | vitest v4 + @testing-library/react | jest |
Prisma 7 uses a config file and driver adapters — this is already set up:
prisma.config.ts— CLI config, loads.envvia dotenvprisma/schema.prisma— generator outputs tosrc/generated/prisma(gitignored)@prisma/adapter-better-sqlite3— SQLite driver adapter (swap for@prisma/adapter-pgon Postgres)
The client singleton pattern for this project:
// src/lib/prisma.ts
import { PrismaClient } from "@/generated/prisma/client";
import { PrismaBetterSQLite3 } from "@prisma/adapter-better-sqlite3";
const adapter = new PrismaBetterSQLite3({ url: process.env.DATABASE_URL! });
const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };
export const prisma = globalForPrisma.prisma ?? new PrismaClient({ adapter });
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;Commands: npm run db:generate, npm run db:push, npm run db:migrate, npm run db:studio.
The v5 pattern — one config, exported helpers, thin route handler:
// src/lib/auth.ts
import NextAuth from "next-auth";
import GitHub from "next-auth/providers/github";
import { PrismaAdapter } from "@auth/prisma-adapter";
import { prisma } from "@/lib/prisma";
export const { handlers, auth, signIn, signOut } = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [GitHub],
});Env vars use the AUTH_* convention (AUTH_SECRET, AUTH_GITHUB_ID, …) — see .env.example. Generate a secret with npx auth secret.
Components are copied into the repo, not installed as dependencies — you own the code:
npx shadcn@latest add button card input label form dialog tablecomponents.json is generated by /2-setup-foundation (Tailwind v4 style: css-based config, no tailwind.config.js). Installed parts land in src/components/ui/. Compose with the cn() helper from src/lib/utils.ts and design tokens only:
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
<Button className={cn("font-body", isUrgent && "bg-destructive")}>
Send quote
</Button>;The Radix primitives shadcn depends on (dialog, select, dropdown-menu, tabs, popover, toast, checkbox, switch, slot) are pre-installed.
Config is CSS-first. src/app/globals.css (generated by /2-setup-foundation) holds:
@import "tailwindcss"andtw-animate-css- OKLCH color variables for light (
:root) and dark (.dark) @theme inlinemapping every token to a utility — includingfont-heading,font-body, and theds-1–ds-6spacing steps
DESIGN_SYSTEM.md documents the full vocabulary. Off-system values are blocked by both ESLint rules and the PostToolUse hook.
- Reads: server components fetch directly via Prisma; client-side refetching goes through TanStack Query
- Writes: server actions validated with zod; route handlers (
src/app/api/*/route.ts) for webhooks/external consumers - Client state: zustand stores in
src/stores/, one store per domain - External APIs: axios, called from the server side whenever a key is involved
npm run dev/build/start— Next.js 16 with Turbopacknpm run lint/lint:fix— ESLint 9 flat config with design-system rulesnpm run type-check— strict TypeScriptnpm test/test:ui/test:coverage— vitest + Testing Library (jsdom)npm run format— Prettier (with Tailwind class sorting)npm run analyze— bundle analysisnpm run validate-design-system— self-test of the enforcement layers- husky + lint-staged run lint and format on every commit