Using Clerk for Authentication

Sep 1, 2025

I was implementing an authentication in my Next.js project with Clerk when I realized some of the syntax that I was using were deprecated.

So I decided to use this chance to document Clerk + Next.js.

Setting Up Clerk

1.Install @clerk/nextjs

npm install @clerk/nextjs

2.Add clerkMiddleware() to app

clerkMiddleware() grants access to user auth state throughout the app

Make middleware.ts and put

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)(.*)',
  ],
}

3.Add ClerkProvider and Clerk components to your app

In layout.tsx

import { ClerkProvider } from "@clerk/nextjs";

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <ClerkProvider>
      <html lang="en">
        <body
          className={`${geistSans.variable} ${geistMono.variable} antialiased bg-background`}
        >
          {children}
        </body>
      </html>
    </ClerkProvider>
  );
}

4.Build Sign-in-page

In app/sign-in/[[...sign-in]]/page.tsx

import { SignIn } from '@clerk/nextjs'

export default function Page() {
  return <SignIn />
}

5.Making sign-in route public

In middleware.ts

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

const isPublicRoute = createRouteMatcher(['/sign-in(.*)'])

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 makes only /sign-in public route and rest are private route that cannot be accessed with out credential

6.Update your environmental variables

  • Set the CLERK_SIGN_IN_URL environmental variable to tell Clerk where the SignIn component is being hosted
  • (I didn't) but set CLERK_SIGN_IN_FALLBACK_REDIRECT_URL as a fallback URL incase iusers visit the /sign-in route directly
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up

And with this I quickly implemented my project's authentication 🎉

Steve Jin