Tutorial··15 min
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 — in one afternoon.
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
Total time: 2-4 hours depending on how much you customise. Claude Code handles probably 80% of the actual typing.
Prerequisites
--------------
- Node.js 18+, Claude Code installed (npm install -g @anthropic-ai/claude-code)
- A Supabase account (free tier works)
- A Vercel account (free tier works)
- Git + GitHub account
Step 1 — Scaffold the project
------------------------------
npx create-next-app@latest my-app --typescript --tailwind --app --eslint
cd my-app
claude
Once Claude Code starts, install the right skill first:
claude skills add nextjs-app-router-pro
This gives Claude Code pre-loaded context about Next.js 14 patterns — App Router, Server Components, Server Actions, Tailwind. You won't need to explain these every session.
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 and update next.config.mjs with the matcher. 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. The first deployment often has one env var mistake — Vercel's function logs will tell you exactly which one.
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) pre-load domain context and make the agent faster
- 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.