Build a full-stack Next.js app with Claude Code — complete walkthrough
Real step-by-step: use Claude Code to scaffold, build, and deploy a Next.js 14 App Router app with Supabase auth, a protected dashboard, and a public landing page.
By CB Patel
This is a real walkthrough, not a demo. We're building a working Next.js 14 app — public landing page, GitHub OAuth sign-in, protected dashboard — using Claude Code as the primary coding agent. Every command is real. Every instruction to the agent is exact.
What we're building
- Public landing page (marketing site)
- GitHub OAuth sign-in via Supabase Auth
- Protected /dashboard route
- A simple "saved links" feature (CRUD, stored in Supabase)
- Deploy to Vercel
Prerequisites
- Node.js 18.17 or later (required by Next.js 14)
- Claude Code installed (on macOS or Linux:
curl -fsSL https://claude.ai/install.sh | bash) - A Supabase account (free tier works)
- A Vercel account (free tier works)
- Git + GitHub account
Step 1 — Scaffold the project
This walkthrough targets Next.js 14, so pin that version:
npx create-next-app@14 my-app --typescript --tailwind --app --eslint
cd my-app
Before starting Claude Code, install the right skill into the project:
curl -fsSL --create-dirs -o .claude/skills/nextjs-app-router-pro/SKILL.md https://claudeskil.com/skills/nextjs-app-router-pro/SKILL.md
This gives Claude Code ready-made context about Next.js 14 patterns — App Router, Server Components, Server Actions, streaming, and ISR — which it loads whenever a request matches. You won't need to explain these every session. Then start Claude Code:
claude
Now set up Supabase:
install @supabase/supabase-js and @supabase/ssr. Create lib/supabase/client.ts (browser client) and lib/supabase/server.ts (server client using cookies from next/headers). Use the environment variables NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY.
Review what it produces. The server client should use createServerClient from @supabase/ssr with a cookies() adapter. If it uses the older createClient with cookies manually, ask it to update to the ssr pattern.
Step 2 — Auth middleware
add a middleware.ts in the project root that uses Supabase to refresh the session on every request. Protect all /dashboard/* routes — redirect unauthenticated users to /sign-in. Public routes: /, /sign-in, /sign-up, /api/auth/callback.
Claude Code will write the middleware, including the matcher in the config object exported from middleware.ts. Read the matcher pattern carefully — a wrong regex here means your /api routes get auth-checked, which breaks everything.
Step 3 — Sign-in page
create app/sign-in/page.tsx. It should have a "Sign in with GitHub" button that calls supabase.auth.signInWithOAuth with provider: 'github' and redirectTo pointing to /api/auth/callback. Show a spinner while the redirect is happening. Style it with Tailwind — clean, centered, dark background.
Then create the OAuth callback:
create app/api/auth/callback/route.ts. It handles the code exchange for a Supabase session using exchangeCodeForSession, then redirects to /dashboard.
Step 4 — Protected dashboard
create app/dashboard/layout.tsx. It should get the current user from Supabase on the server using the server client. If no user, redirect to /sign-in. Show a simple sidebar with: Dashboard, Saved Links, Settings. Add a sign-out button that calls supabase.auth.signOut via a Server Action and redirects to /.
create app/dashboard/page.tsx. Show "Welcome back, [user.email]" and a count of the user's saved links (query the links table, count only).
Step 5 — Saved links feature
First, create the database table. Go to your Supabase dashboard > SQL Editor and run:
create table links (
id uuid primary key default gen_random_uuid(),
user_id uuid references auth.users not null,
url text not null,
title text,
created_at timestamptz default now()
);
alter table links enable row level security;
create policy "users can only see their own links"
on links for all
using (auth.uid() = user_id);
Then let Claude Code build the UI:
create app/dashboard/links/page.tsx. Fetch the current user's links from Supabase (server component). Render them in a list. Add a form at the top with a URL input and a title input. The form submission should be a Server Action that inserts a new row into the links table with the current user's id, then revalidatePath('/dashboard/links').
Step 6 — Landing page
create app/page.tsx — a marketing landing page. Hero section with headline "Save links that matter" and a "Get started free" CTA that links to /sign-in. Three feature sections (Clean UI, Private by default, Works everywhere). A simple footer. Tailwind dark theme.
At this point, run the dev server:
npm run dev
Test the full flow manually: landing → sign in → dashboard → add a link → see it appear.
Step 7 — Fix the inevitable issues
Something will be wrong. Common ones:
If the OAuth redirect goes to localhost in production: set the NEXT_PUBLIC_APP_URL env var and update the redirectTo to use it.
If the Server Action isn't revalidating: make sure revalidatePath is imported from next/cache, not next/navigation.
If the middleware redirects are looping: check that your matcher doesn't include /api/auth/callback — that route must be public or the session exchange never completes.
Ask Claude Code to fix each issue:
the OAuth redirect is using localhost:3000 even in production. fix it to use NEXT_PUBLIC_APP_URL with a fallback to localhost:3000.
Step 8 — Deploy to Vercel
npx vercel --prod
Set the environment variables in the Vercel dashboard:
NEXT_PUBLIC_SUPABASE_URL = from your Supabase project settings
NEXT_PUBLIC_SUPABASE_ANON_KEY = from your Supabase project settings
NEXT_PUBLIC_APP_URL = https://your-app.vercel.app
In your Supabase Auth settings, add the Vercel URL to the allowed redirect URLs.
Test the full flow on production.
What you've learned
By doing this walkthrough you've experienced:
- How to write precise, effective instructions for Claude Code
- Where the agent is reliable (boilerplate, patterns, CRUD) vs. where you need to supervise (auth flows, env var wiring)
- How skills (nextjs-app-router-pro) give the agent domain context
- The review habit: read every diff, especially on auth-related code
The source code for this walkthrough is the kind of thing you could turn into a SKILL.md yourself — "Full-stack Next.js with Supabase auth" as a reusable pattern. That's exactly how the skills marketplace grows.
Explore more Next.js skills at claudeskil.com/category/fullstack.