DEV Community

Cover image for Setting up Supabase Auth with Next.js in 10 minutes
Krishna Shrestha
Krishna Shrestha

Posted on

Setting up Supabase Auth with Next.js in 10 minutes

Every time I started a new project, auth killed my momentum.

Setting up JWT, hashing passwords, managing sessions,
handling refresh tokens... before writing a single line
of actual product code I'd already wasted 2 days.

Then I found Supabase Auth. I had a fully working
login system in my Next.js app in 10 minutes flat.
Here's exactly how.


Why auth is such a pain normally

If you've ever built auth from scratch you know the drill:

  • Install bcrypt, hash every password manually
  • Set up JWT, handle token expiry, refresh tokens
  • Store sessions, manage cookies securely
  • Handle edge cases like "what if the token expires mid-session"
  • Pray nothing is vulnerable

It's not that it's impossible. It's that it's boring,
repetitive work that has nothing to do with your
actual product.

Supabase handles ALL of that for you.
You just call a function. That's it.


What we're building

  • Email/password authentication
  • Protected routes
  • Session management
  • Sign in, Sign up, Sign out

Prerequisites

  • Next.js app set up
  • Supabase account (free at supabase.com)
  • Basic knowledge of React

Step 1 — Create a Supabase project

  1. Go to supabase.com
  2. Click New Project
  3. Fill in your project name and database password
  4. Wait for it to spin up (~2 minutes)

Once ready, go to Settings → API and copy:

  • Project URL
  • anon public key

Step 2 — Install Supabase in your Next.js app

npm install @supabase/supabase-js @supabase/ssr
Enter fullscreen mode Exit fullscreen mode

Step 3 — Set up environment variables

Create a .env.local file in your root:

NEXT_PUBLIC_SUPABASE_URL=your_project_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key
Enter fullscreen mode Exit fullscreen mode

Don't commit this to GitHub. Make sure .env.local
is in your .gitignore — if it's not, add it now.


Step 4 — Create the Supabase client

Create lib/supabase.js:

import { createBrowserClient } from '@supabase/ssr'

export function createClient() {
  return createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
  )
}
Enter fullscreen mode Exit fullscreen mode

I keep this in a lib folder so I can import it
anywhere in the app without repeating myself.


Step 5 — Sign up page

Create app/signup/page.jsx:

'use client'

import { useState } from 'react'
import { createClient } from '@/lib/supabase'
import { useRouter } from 'next/navigation'

export default function SignUp() {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [error, setError] = useState(null)
  const router = useRouter()
  const supabase = createClient()

  const handleSignUp = async () => {
    const { error } = await supabase.auth.signUp({ email, password })
    if (error) {
      setError(error.message)
    } else {
      router.push('/dashboard')
    }
  }

  return (
    <div>
      <h1>Create account</h1>
      <input
        type="email"
        placeholder="your@email.com"
        onChange={(e) => setEmail(e.target.value)}
      />
      <input
        type="password"
        placeholder="password"
        onChange={(e) => setPassword(e.target.value)}
      />
      {error && <p style={{ color: 'red' }}>{error}</p>}
      <button onClick={handleSignUp}>Sign Up</button>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Nothing fancy. Grab email and password, pass it to
Supabase, redirect to dashboard. Done.


Step 6 — Sign in page

Create app/signin/page.jsx:

'use client'

import { useState } from 'react'
import { createClient } from '@/lib/supabase'
import { useRouter } from 'next/navigation'

export default function SignIn() {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [error, setError] = useState(null)
  const router = useRouter()
  const supabase = createClient()

  const handleSignIn = async () => {
    const { error } = await supabase.auth.signInWithPassword({
      email,
      password
    })
    if (error) {
      setError(error.message)
    } else {
      router.push('/dashboard')
    }
  }

  return (
    <div>
      <h1>Welcome back</h1>
      <input
        type="email"
        placeholder="your@email.com"
        onChange={(e) => setEmail(e.target.value)}
      />
      <input
        type="password"
        placeholder="password"
        onChange={(e) => setPassword(e.target.value)}
      />
      {error && <p style={{ color: 'red' }}>{error}</p>}
      <button onClick={handleSignIn}>Sign In</button>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Quick note — make sure you use signInWithPassword
not signIn. I spent 20 minutes confused the first
time because of this 😅 Supabase has multiple sign
in methods so you have to be specific.


Step 7 — Protect your dashboard

This is the part most tutorials skip completely.
What's the point of auth if anyone can just
go to /dashboard directly in the URL bar?

Create app/dashboard/page.jsx:

import { createClient } from '@/lib/supabase'
import { redirect } from 'next/navigation'

export default async function Dashboard() {
  const supabase = createClient()
  const { data: { session } } = await supabase.auth.getSession()

  if (!session) {
    redirect('/signin')
  }

  return (
    <div>
      <h1>Hey {session.user.email} 👋</h1>
      <p>You're in.</p>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

No session? Get out. Simple as that.


Step 8 — Sign out

Add this wherever makes sense in your app:

const supabase = createClient()

const handleSignOut = async () => {
  await supabase.auth.signOut()
  router.push('/signin')
}

<button onClick={handleSignOut}>Sign out</button>
Enter fullscreen mode Exit fullscreen mode

You just saved yourself 2 days

Most developers waste hours on auth before
writing a single feature. You just skipped
all of that in 10 minutes.

Your users can now sign up, log in, and access
protected pages. That's a real app.
Now go build the actual product.

In under 10 minutes you now have:

  • ✅ Email/password sign up
  • ✅ Sign in with session management
  • ✅ Protected dashboard route
  • ✅ Sign out functionality

The bigger picture

Auth is just the beginning of what Supabase can do.
Once your users can log in you've got a real app —
now you can start building the actual product.

In my next article I'll show you how to:

  • Add Google OAuth in 3 lines
  • Create a user profile table that auto-links to auth
  • Handle protected API routes in Next.js

If you're building a SaaS product and feeling stuck,
I write about real problems I run into while building
with Next.js and Supabase.

I'm Krishna — full stack dev from Kathmandu, Nepal.
I've been building SaaS products and helping clients
turn ideas into real web apps.

Got a project in mind? Let's talk →
krishna011.com.np

Top comments (0)