// starters/claude.md/claude-md-nextjs-supabase

claude.md — next.js + supabase

CLAUDE.md template for the most common vibe-coder stack — Next.js App Router + Supabase + Vercel. Stack-specific gotchas, conventions, the "don't do this" list.

last fed 28 may 2026960 words · 5 min read
// when to use this starter

Use this when starting a Next.js + Supabase project, or when retrofitting one that has a vanilla CLAUDE.md the AI keeps ignoring. Replace the {placeholders} with your project's specifics; the structure is the durable part.

nextjssupabasevercelclaude-mdstack

CLAUDE.md

Context file for AI agents working in this repository. Read this first. This is a sourdough starter — fed as the project evolves, not set-and-forget. Last meaningful update: 2026-05-28.


What this is

{Your project name} — {one-sentence technical identity}. {Audience / what it does for them in one more sentence}.

Three deployable surfaces:

  • app/ — Next.js App Router (marketing + dashboard), deployed on Vercel
  • supabase/ — schema migrations + RLS policies, applied via Supabase CLI
  • workers/ — Cloudflare Workers for tool execution (if applicable; delete if not)

Stack

Layer Tech Notes
Framework Next.js 15+ App Router + TypeScript Strict TS, no JS files. Server components by default.
UI primitives {shadcn/ui? Raw Tailwind? Custom CSS classes?} Be specific.
Fonts {Inter? JetBrains Mono?} Loaded via next/font/google in lib/fonts.ts
Database + Auth Supabase RLS on every table. No exceptions.
Runtime secrets Supabase Vault Used for any secret that crosses a deploy.
Dev-time secrets .env.local (gitignored) Never in committed files.
Email {Resend? Postmark?} Configured in lib/email/*
Errors {Sentry? Custom?} All three surfaces if applicable.
Payments {Stripe? Lemon Squeezy?} Phase N if not yet wired.

Standing rules (apply always)

These are non-negotiable across the codebase. The AI should treat them as constraints, not suggestions.

  1. No secret in any file that touches git. .env.local is gitignored; Vault for runtime secrets.
  2. Supabase RLS on every table. No exceptions. Even tables not yet user-facing.
  3. No any types. Use unknown and narrow, or define the type properly.
  4. Server components by default. Add 'use client' only when you need state, effects, or browser APIs.
  5. {Project-specific copy rule} — e.g. "Lowercase headings in marketing copy" or "No emojis in code or copy unless I explicitly asked".
  6. Don't add comments that restate what the code does. Only comment WHY when the why isn't obvious.
  7. No new abstractions on first use. Three similar lines beats a premature helper.
  8. Mobile-first responsive. Every page works at 360px width.
  9. Test in the browser before claiming a UI task is done. Type checks aren't behaviour checks.

Folder structure

{project}/
├── app/                        ← Next.js App Router
│   ├── (marketing)/            ← Public pages
│   ├── (dashboard)/            ← Authed pages
│   ├── api/                    ← Route handlers
│   ├── layout.tsx              ← Root layout
│   └── globals.css
├── components/
│   ├── ui/                     ← Primitives (shadcn or your own)
│   └── {domain}/               ← Domain components grouped by area
├── lib/
│   ├── supabase/               ← Server + client supabase factories
│   ├── {domain}.ts             ← Domain-specific utilities
│   └── utils.ts
├── supabase/
│   └── migrations/             ← SQL migrations, numeric-prefix order
└── public/

Next.js + Supabase gotchas (the ones that bite)

These are real failure modes the AI introduces by default. Pre-record them so they don't keep happening.

  • NEXT_PUBLIC_ prefix on a Supabase service-role key exposes it to the browser. The other env vars are public; the pattern misleads. Service-role keys belong server-side only, in routes / server components / route handlers — never in client code.
  • createClient() from @supabase/ssr vs @supabase/supabase-js matters. Server components need the server-side factory (passes cookies); client components need the client factory. Mixing them silently breaks auth.
  • RLS policies need WITH CHECK on UPDATE/INSERT. USING only covers SELECT-shaped reads. The row-reassignment bug ships when WITH CHECK is missing — the AI scaffolds RLS as a checkbox and forgets WITH CHECK.
  • auth.role() = 'authenticated' is not the same as auth.uid() = user_id. The first lets any logged-in user through; only the second scopes to the owner.
  • Server Actions need explicit 'use server'. The AI sometimes forgets the directive; the action then runs client-side and exposes whatever secrets it touched.
  • Cookie-based auth and revalidation cache interact badly. When you call revalidatePath('/some-route') after an auth-state change, force cache: 'no-store' on any fetch that reads the cookie session, or you'll serve stale auth state.

Naming + conventions

Thing Convention Example
Component files kebab-case .tsx tool-card.tsx, nav.tsx
Component names PascalCase ToolCard, Nav
SQL tables snake_case, plural profiles, api_keys, tool_runs
Env vars UPPER_SNAKE_CASE STRIPE_SECRET_KEY, NEXT_PUBLIC_SUPABASE_URL
Migrations NNN_kebab-name.sql 001_initial_schema.sql
Tool MCP names (if applicable) {project}__action_object fizzgig__rls_checker

Don't do this

  • Don't introduce lib/services/ or lib/repositories/ layers. Premature abstraction.
  • Don't disable RLS as a workaround. Fix the policy.
  • Don't trust input from the client because it was validated client-side. Server validates separately.
  • Don't recreate the marketing pages — they're done. Edit existing files.
  • Don't commit secrets, even in test fixtures. Use .env.local (gitignored) or Vault.

Where to look when

If you need to… Go to
Understand WHY a decision was made docs/adr/ (architecture decision records)
Understand WHAT we're building this week {wherever your sprint plan lives}
Understand the schema latest migration in supabase/migrations/
Understand the design system app/globals.css + components/ui/
Understand security posture [starters/security-posture](https://fizzgig.ai/starters/security-posture)

Feed log

Notable updates to this CLAUDE.md. Most recent first. Each entry: what changed, why.

  • 2026-05-28 — Initial fork from fizzgig.ai/starters/claude-md-nextjs-supabase.

This is a sourdough starter — fed as the project evolves. If you're an agent reading this and the project has clearly moved past what this file describes, flag it (the file is stale and needs feeding).