Zero Hassle Auth: Clerk + Next.js (With Repo)

Ashish RautAshish Raut
6 min read

As I moved into Next.js, I thought a different framework might solve the problem. But I soon discovered that even popular solutions like NextAuth had a steep learning curve and a lot of complex configuration, especially for a new developer.

Then, I found Clerk. It completely changed the game. Instead of wrestling with boilerplate code and complex setups, I could now add full authentication—user sign-up, sign-in, and profile management—in minutes. With just a few lines of code and some pre-built, customizable components, my application had a secure, modern authentication system. This simple, copy-and-paste approach frees you up to focus on building the features that truly matter, making the entire development process faster and far more enjoyable.

GitHub Repo

Create Your Clerk Account

Now that you're convinced of the magic, let's get our hands dirty. The beauty of Clerk is that the initial setup is incredibly fast. We'll go from a blank canvas to a fully authenticated app in just a few minutes.

First things first, you'll need a Clerk account. It's free to get started.

  1. Head over to the Clerk website ans sign up.

  2. Once in your dashboard, create a new application. You can give it a like "First Step"

  3. Clerk will automatically generate your API keys. You'll see a Publishable Key and a Secret Key. These are crucial for connecting your Next.js app to Clerk, so keep them safe! We'll use them in the next step.

💡
Once this is done, the rest is all code, and it's surprisingly minimal.

💡
You can choose how many sign-in providers you want to enable for your app. A good starting point is with popular options like Google, Microsoft, or GitHub. If you decide to enable additional providers, keep in mind that you may require a paid plan.

Optional: Creating a Next.js App

npx create-next-app@latest

After running this command, you'll be prompted with a series of questions to configure your new Next.js project, including options for TypeScript, ESLint, Tailwind CSS, and the App Router. This process quickly sets up a clean project structure, getting you ready to code in minutes.

The Clerk Integration: Just a Few Lines of Code

Step 1 - Install Clerk

npm install @clerk/nextjs

Step 2 - Set your Clerk API keys

For a Next.js project, your .env files should be placed in the root of the project (same level as package.json), not inside the app/ folder.

NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=********
CLERK_SECRET_KEY=********
💡
Clerk provides you with your own set of authentication keys that act like passwords for your application. These are unique to your project and must be kept safe.

Step 3 - Update middleware.ts

For a Next.js project, your middleware.ts files should be placed in the root of the project (same level as package.json), not inside the app/ folder.

import { clerkMiddleware } from '@clerk/nextjs/server';

export default clerkMiddleware();

export const config = {
  matcher: [
    // Skip Next.js internals and all static files, unless found in search params
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    // Always run for API routes
    '/(api|trpc)(.*)',
  ],
};

Step 04 - Add ClerkProvider in layout.tsx file

import { type Metadata } from 'next'
import {
  ClerkProvider,
  SignInButton,
  SignUpButton,
  SignedIn,
  SignedOut,
  UserButton,
} from '@clerk/nextjs'
import { Geist, Geist_Mono } from 'next/font/google'
import './globals.css'

const geistSans = Geist({
  variable: '--font-geist-sans',
  subsets: ['latin'],
})

const geistMono = Geist_Mono({
  variable: '--font-geist-mono',
  subsets: ['latin'],
})

export const metadata: Metadata = {
  title: 'Clerk Next.js Quickstart',
  description: 'Generated by create next app',
}

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode
}>) {
  return (
    <ClerkProvider>
      <html lang="en">
        <body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
          <header className="flex justify-end items-center p-4 gap-4 h-16">
            <SignedOut>
              <SignInButton />
              <SignUpButton>
                <button className="bg-[#6c47ff] text-ceramic-white rounded-full font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 cursor-pointer">
                  Sign Up
                </button>
              </SignUpButton>
            </SignedOut>
            <SignedIn>
              <UserButton />
            </SignedIn>
          </header>
          {children}
        </body>
      </html>
    </ClerkProvider>
  )
}

Step 05 - Launch your Project 🎉

npm run dev

Open Clerk Dashboard

💡
When users sign up or log in through your app, all their details will appear in the Clerk Dashboard in real-time.

This means you don’t need to manually manage user accounts — Clerk provides a ready-made user management system where you can view:

  • A list of all authenticated users

  • Their email addresses or OAuth provider (Google, GitHub, Microsoft, etc.)

  • Their status (active, invited, etc.)

Securing Your App: Defining Public and Protected Paths

Update you middleware.ts file

import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'

const isPublicRoute = createRouteMatcher([
    '/',              // only app/page.tsx is public and rest routes like app/dashboard.tsx in protected. Here protected means only authenticated users are allowed.
])

export default clerkMiddleware(async (auth, req) => {
    if (!isPublicRoute(req)) {
        await auth.protect()
    }
})

export const config = {
    matcher: [
        // Skip Next.js internals and all static files, unless found in search params
        '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
        // Always run for API routes
        '/(api|trpc)(.*)',
    ],
};

This middleware protects all routes and APIs by default, except for explicitly defined public routes (like /sign-in) and static assets.

If a user is not authenticated and tries to access a protected route like /dashboard
Clerk’s auth.protect() will block them and redirect them to the sign-in page (/sign-in) by default.

If a user is authenticated, they can access /dashboard normally (no redirect).

Building a Full-Stack App with User-Specific Data

With our routes now securely guarded, an authenticated user can step into protected areas like their dashboard. But access is only half the story; the real magic lies in creating a personalized experience. This is where the userId becomes the cornerstone of our application. Provided by Clerk's auth() helper on the server, this unique identifier is the golden key we use to query our database, ensuring we fetch only the notes, projects, or settings that belong exclusively to that user.

// app/dashboard/page.tsx

import { redirect } from 'next/navigation';
import { auth } from '@clerk/nextjs';
import { prisma } from '@/lib/db';

export default async function DashboardPage() {
  const { userId } = auth();

  // If there's no user, redirect them to the homepage.
  if (!userId) {
    // 2. Replace the returned div with the redirect function
    redirect('/');
  }

  // If the code reaches this point, you are guaranteed to have a userId.
  const userTodos = await prisma.todo.findMany({
    where: {
      userId: userId,
    },
    orderBy: {
      createdAt: 'desc',
    },
  });

  return (
    <div>
      <h1 className="text-3xl font-bold mb-4">Your Todos</h1>
      {/* ... rest of your component to display the todos ... */}
    </div>
  );
}

That's a Wrap!

And there you have it! We've connected all the dots: from a secure login with Clerk to fetching personalized user data. You now hold the fundamental pattern for building truly dynamic and secure web applications. Explore more

I sincerely hope you found this guide valuable and love the final result of your work. Happy coding.

GitHub Repo

GitHub

X (Formerly Twitter)

LinkedIn

0
Subscribe to my newsletter

Read articles from Ashish Raut directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ashish Raut
Ashish Raut