How to Implement Supabase Auth Login in a Next.js App
If you’ve been dabbling with server‑less backends, you’ve probably heard of Supabase. It offers a PostgreSQL database, real‑time subscriptions, and—most importantly for this guide—a fully‑featured authentication layer that works smoothly with modern React frameworks. In this article we’ll walk through a step‑by‑step, Supabase Auth login with Next.js that feels like a native part of your app, not an afterthought. By the end you’ll have a reusable auth context, a clean login form, and protected routes that respect both client‑side and server‑side rendering.
Setting Up Your Supabase Project
First, head over to supabase.com and create a new project. Choose a project name, pick a password‑protected database, and wait a couple of minutes for the instance to spin up. Once it’s ready, navigate to the “Authentication” tab and enable the email‑password provider—this is the simplest method for a tutorial. Copy the API URL and the anon public key; you’ll need them when you configure the client in Next.js.
Installing Packages in Your Next.js Project
Open a terminal in the root of your Next.js app and run:
npm install @supabase/supabase-jsnpm install react-hook-form(optional, but handy for form handling)
Both packages are lightweight, and the Supabase client works out of the box with Next.js’s built‑in fetch implementation. No extra polyfills are required.
Configuring the Supabase Client
Create a file called lib/supabaseClient.js. Inside, import the library and instantiate a single client using the URL and anon key you saved earlier. Export the client so you can import it anywhere—components, API routes, or server‑side functions.
Because the client is a singleton, you avoid multiple connections and keep your auth state consistent across page navigations.
Creating an Auth Context
Next, set up a React context that will hold the current user and a few helper functions. In context/AuthContext.js, wrap your _app.js with AuthProvider. Inside the provider, use supabase.auth.onAuthStateChange to listen for sign‑in, sign‑out, and token refresh events. When the state changes, update a piece of state that stores the user object and a loading flag.
Providing this context makes it trivial to check authentication status from any component without prop‑drilling.
Building the Login Form
Now for the UI. In components/LoginForm.js, use react-hook-form to capture email and password inputs. On submit, call supabase.auth.signInWithPassword({ email, password }). If the request succeeds, the auth listener you set up earlier will automatically update the context, triggering a re‑render that redirects the user to the dashboard.
Don’t forget to surface error messages—Supabase returns clear error codes that you can map to friendly text for your users.
Handling Sessions and Redirects
Because Next.js supports both client‑side navigation and server‑side rendering, you’ll want to guard pages on both sides. On the client, a simple useEffect that watches the auth context can push unauthenticated users to /login. On the server, use getServerSideProps to call supabase.auth.getSession() (available in the newer SDK). If there’s no valid session, return a redirect object.
Protecting Server‑Side Pages
For a page like pages/dashboard.js, add:
- Exported
getServerSidePropsthat checks the session. - A fallback UI that shows a spinner while the auth state is loading.
This pattern ensures that SEO crawlers and users with JavaScript disabled still see the correct content—or are safely redirected.
Common Pitfalls and How to Avoid Them
Forgot to set the redirect URL: Supabase needs an allowed redirect domain in the auth settings. Add http://localhost:3000 for local development and your production domain before testing.
Stale auth listener: If you mount multiple providers (unlikely, but possible), you might end up with duplicate listeners. Clean them up by returning the unsubscribe function from useEffect.
SSR token mismatch: The server‑side SDK uses cookies to read the session. Make sure supabase.auth.setAuth(cookie) runs early in getServerSideProps so the token is attached to subsequent API calls.
Putting It All Together
At this point you have a functional authentication flow: a Supabase‑backed login form, a global auth context, client‑side redirects, and server‑side protection. The same pattern scales nicely to other auth methods—Google, GitHub, Apple—by swapping the sign‑in call for supabase.auth.signInWithOAuth({ provider: 'github' }) and handling the callback URL in pages/api/auth/callback.js.
Remember to keep your environment variables secure: store the Supabase URL and anon key in .env.local and never commit them to version control. With that precaution in place, you’re ready to ship a production‑grade login experience powered by Supabase and Next.js.
FAQ
Can I use Supabase Auth with Next.js’s static site generation (SSG)?
SSG builds pages at build time, so it can’t know a user’s session. However, you can still render a public version of the page and let the client‑side auth context take over after hydration. For truly private data, prefer SSR or API routes that verify the token on each request.
Do I need a server to handle password resets?
No separate server is required. Supabase provides built‑in email templates; just call supabase.auth.resetPasswordForEmail(email) and Supabase sends the link. Your Next.js app only needs a page to capture the token from the URL and call supabase.auth.updateUser({ password }).
Is the anon public key safe to expose in the client?
Yes. The anon key is designed for client‑side usage and only grants access to operations allowed by your Supabase policies. Always enforce row‑level security (RLS) to ensure users can’t read or write data they shouldn’t.