Next.js 15 + Supabase Auth + PWA
Complete implementation guide for adding Supabase authentication to a Next.js 15 App Router PWA
š Overview
This application serves as a complete reference implementation for integrating Supabase authentication into a Next.js 15 Progressive Web App.
Tech Stack:
- ā Next.js 15.5.4 (App Router)
- ā React 19.1.0
- ā Supabase Auth (@supabase/ssr)
- ā TypeScript
- ā Tailwind CSS v4
- ā PWA Support (@ducanh2912/next-pwa)
šļø Architecture
Auth Flow
- User signs up with email/password
- Supabase sends confirmation email
- User clicks confirmation link
- Route handler verifies token and logs user in
- Middleware refreshes session on every request
- Session stored in cookies (httpOnly, secure)
Key Components
- Server Client: Used in Server Components, Route Handlers, and Server Actions
- Browser Client: Used in Client Components ("use client")
- Middleware Client: Refreshes sessions on every request
- Middleware: Runs before every page load to maintain auth state
š Implementation Guide (7 Phases)
Follow these phases in order. Each phase builds on the previous one. Test pages are provided to verify each phase works correctly.
Phase 1: Setup & Dependencies
Install packages, configure environment, verify TypeScript paths
⢠Install @supabase/ssr and @supabase/supabase-js
⢠Configure .env.local with Supabase credentials
⢠Verify tsconfig.json path aliases
⢠Create lib/utils.ts
Phase 2: Supabase Clients
Create server, browser, and middleware Supabase clients
⢠Create lib/supabase/server.ts
⢠Create lib/supabase/client.ts
⢠Create lib/supabase/middleware.ts
Phase 3: Middleware
Set up middleware to refresh auth sessions automatically
⢠Create src/middleware.ts
⢠Configure matcher to exclude static files
⢠Implement session refresh on every request
Phase 4: Auth Routes
Create authentication pages and route handlers
⢠Create /auth/login page
⢠Create /auth/sign-up page
⢠Create /auth/confirm route handler
⢠Create /auth/error and /auth/sign-up-success pages
Phase 5: Auth Components & Forms
Build login, signup, logout components with full functionality
⢠Create LoginForm component
⢠Create SignUpForm component
⢠Create LogoutButton component
⢠Create AuthButton component
⢠Add AuthButton to header
Phase 6: Supabase Dashboard Config
Configure production redirect URLs and optional settings
⢠Add production URLs to Supabase redirect list
⢠Configure Site URL
⢠Optional: Customize email templates
⢠Optional: Configure custom SMTP
Phase 7: Protected Pages & Integration
Add protected pages, feature gating, and final testing
⢠Create protected page examples
⢠Implement feature gating patterns
⢠End-to-end integration testing
⢠Production readiness checklist
š Key Files & Structure
src/ āāā middleware.ts āāā lib/ ā āāā supabase/ ā ā āāā server.ts ā ā āāā client.ts ā ā āāā middleware.ts ā āāā utils.ts āāā components/ ā āāā login-form.tsx ā āāā sign-up-form.tsx ā āāā logout-button.tsx ā āāā auth-button.tsx āāā app/ ā āāā auth/ ā ā āāā login/page.tsx ā ā āāā sign-up/page.tsx ā ā āāā confirm/route.ts ā ā āāā error/page.tsx ā ā āāā sign-up-success/page.tsx ā āāā components/ ā āāā Header.tsx ā āāā NavDropdown.tsx āāā .env.local
Key files explained:
- ⢠middleware.ts - Session refresh middleware
- ⢠lib/supabase/server.ts - Server-side client
- ⢠lib/supabase/client.ts - Browser-side client
- ⢠lib/supabase/middleware.ts - Middleware client
- ⢠components/ - Auth forms and buttons
- ⢠app/auth/ - Authentication pages and handlers
š» Common Code Patterns
Check Auth in Server Component
import { createClient } from "@/lib/supabase/server";
export default async function MyPage() {
const supabase = await createClient();
const { data } = await supabase.auth.getUser();
const user = data?.user;
if (!user) {
return <div>Please log in</div>;
}
return <div>Welcome, {user.email}!</div>;
}Check Auth in Client Component
"use client";
import { createClient } from "@/lib/supabase/client";
import { useEffect, useState } from "react";
export function MyComponent() {
const [user, setUser] = useState(null);
useEffect(() => {
const supabase = createClient();
supabase.auth.getUser().then(({ data }) => {
setUser(data.user);
});
}, []);
return user ? <div>Logged in!</div> : <div>Not logged in</div>;
}Protected Route Pattern
import { createClient } from "@/lib/supabase/server";
import { redirect } from "next/navigation";
export default async function ProtectedPage() {
const supabase = await createClient();
const { data } = await supabase.auth.getUser();
if (!data.user) {
redirect("/auth/login");
}
return <div>Protected content for {data.user.email}</div>;
}š§ Troubleshooting
Email confirmation not working?
Check that your production URL is added to Supabase redirect URLs with the /** wildcard
Session not persisting?
Ensure middleware is configured correctly and calling updateSession()
Random logouts?
Make sure middleware is calling await supabase.auth.getClaims() to refresh the session
TypeScript errors with env vars?
Add ! assertion: process.env.NEXT_PUBLIC_SUPABASE_URL!
š Resources
This implementation guide serves as a reference for developers implementing Supabase authentication in Next.js 15 Progressive Web Apps.
All code is tested and production-ready. Feel free to use as a template! š