Cover Image for Add Authentication and User Management in a Next.js App Router Project with Clerk

Add Authentication and User Management in a Next.js App Router Project with Clerk

Lauro Silva
Lauro Silva
Time to read6 min read
Published1y ago

Introduction

This article will include additional context on what I learned as I added Clerk to Frontend Dot Dev, a Next.js App Router project. The documentation is pretty straightforward, but I found some steps needing more explanation, specifally around my use case.

The Advantages of Using Clerk

Honestly, I didn't want to spend months building my auth system. I wanted to focus on building the core features of my application. I also wanted to use a third party auth solution that would allow me to customize the user experience to match my brand. Clerk provides all of this and more. The built-in best practices in their <SignIn/> and <UserProfile/> components are so comprehensive. I was able to get up and running in less than an hour.

Integrate Clerk into a Next.js App Router Project

  1. Create a Clerk account. You can do this by going to clerk.dev and clicking on the "Sign Up" button.

  2. Install Clerk in your project. The @clerk/nextjs package provides the necessary tools and components to integrate Clerk's services into a Next.js application.

1pnpm add @clerk/nextjs
  1. Set environment keys in .env.local:
1NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=​pk_test_••••••••••••••••••••••••••••••••••​
2CLERK_SECRET_KEY=​sk_test_••••••••••••••••••••••••••••••••••​
  1. Wrap your app in <ClerkProvider> in app/layout.tsx:
src/app/layout.tsx
1import './globals.css'
2import {ClerkProvider} from '@clerk/nextjs'
3
4export default function RootLayout({children}: {children: React.ReactNode}) {
5 return (
6 <ClerkProvider>
7 <html suppressHydrationWarning lang="en">
8 <body>
9 <main>
10 <Navigation />
11 {children}
12 <Analytics />
13 </main>
14 </body>
15 </html>
16 </ClerkProvider>
17 )
18}
  1. Require authentication to access your app by creating middleware.ts file at the root of your project. The middleware.ts file is used to configure the Clerk middleware.
src/middleware.ts
1import { authMiddleware } from "@clerk/nextjs";
2
3// This example protects all routes including api/trpc routes
4// Please edit this to allow other routes to be public as needed.
5// See https://clerk.com/docs/references/nextjs/auth-middleware for more information about configuring your Middleware
6export default authMiddleware({});
7
8export const config = {
9 matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'],
10};
11
🚧

If your application has a src/ directory, you should put the middleware.ts file inside this src/ folder else at the root of your project.

That's it! If you visit http://localhost:3000 you will see that Clerk is protecting all the routes and you will be redirected to the sign-in page.

Configure Public Access to Specific Routes

The config object is used to configure the middleware, you can leave this as it is. The matcher property is a regex that matches all routes except for static assets and the Next.js API routes.

src/middleware.ts
1import { authMiddleware } from "@clerk/nextjs";
2export default authMiddleware({});
3
4export const config = {
5 matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'],
6};
7

You can allow other routes to be public by editing the publicRoutes array:

src/middleware.ts
1import {authMiddleware} from '@clerk/nextjs'
2
3export default authMiddleware({
4 publicRoutes: ['/', '/courses/:path*'],
5})
6
7export const config = {
8 matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'],
9}
10

Add '/'. This is the root route of your application. It means that the homepage of your application is accessible to everyone, regardless of whether they are logged in or not.

You can use rejex to match any route you want. In my case, for Frontend Dot Dev I want everyone to have access to the courses route and sub-routes. So I added '/courses/:path*'.

🌟

The :path* part is a path segment wildcard, which matches any number of segments in the path. For example, it would match/courses/css, /courses/css/lesson/1, etc.

Integrate the <UserButton /> Component

The <UserButton /> component to allows users to manage their account information and log out. You can add it anywhere, I'll add to the navbar of my application. When logged in, the <UserButton /> component will display the user's name and avatar.

components/nav.tsx
1import {UserButton} from '@clerk/nextjs'
2import Link from 'next/link'
3
4export default function Navigation({ children }) {
5 return (
6 <>
7 <nav>
8 <ul>
9 <li>
10 <Link href="/">
11 Home
12 </Link>
13 </li>
14 <li>
15 <Link href="/courses">
16 Courses
17 </Link>
18 </li>
19 <li>
20 <UserButton />
21 </li>
22 </ul>
23 </nav>
24 </>
25 )
26}
27

Create Custom Sign In and Sign Up Pages

To create custom sign in page, create the following pages:

1mkdir -p app/sign-in/[[...sign-in]]
2mkdir -p app/sign-up/[[...sign-up]]

Add the following code to sign-in/[[...sign-in]]/page.tsx.

app/sign-in/[[...sign-in]]/page.tsx
1import {SignIn} from '@clerk/nextjs'
2
3export default function Page() {
4 return (
5 <div className="flex h-screen items-center justify-center">
6 <SignIn />
7 </div>
8 )
9}
10

Add the following code to sign-up/[[...sign-up]]/page.tsx.

app/sign-up/[[...sign-up]]/page.tsx
1import {SignUp} from '@clerk/nextjs'
2
3export default function Page() {
4 return (
5 <div className="flex h-screen items-center justify-center">
6 <SignUp />
7 </div>
8 )
9}
10

Update your environment variables in .env.local:

1NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
2NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
3NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/
4NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/
👆

I would recommend adding these variables directly in Vercel and then pulling them into your project using the Vercel CLI. This way they are set when you deploy your project.

Now, visit localhost:3000/sign-in and localhost:3000/sign-up and you will see your custom sign in and sign up pages.

Implement a Pre-Built Clerk Theme

The default theme is great but I want to update it to use the dark pre-built theme. To do that, install the @clerk/clerk-react package, this package provides the pre-built themes.

1pnpm add @clerk/themes

Then, you can update the app/layout.tsx file to use the dark theme by importing it from @clerk/themes and passing it to the baseTheme property of the appearance prop.

app/layout.tsx
1import './globals.css'
2import {ClerkProvider} from '@clerk/nextjs'
3import {dark} from '@clerk/themes'
4
5export default function RootLayout({children}: {children: React.ReactNode}) {
6 return (
7 <ClerkProvider
8 appearance={{
9 baseTheme: dark,
10 }}
11 >
12 <html lang="en">
13 <body>{children}</body>
14 </html>
15 </ClerkProvider>
16 )
17}

Customize component appearance by passing variables to the appearance prop of <ClerkProvider>.

I'll pass in the colorPrimary and colorBackground to match my application's color scheme. But you can also pass other properties, see the Clerk docs for more information.

app/layout.tsx
1import './globals.css'
2import {ClerkProvider} from '@clerk/nextjs'
3import {dark} from '@clerk/themes'
4
5export default function RootLayout({children}: {children: React.ReactNode}) {
6 return (
7 <ClerkProvider
8 appearance={{
9 baseTheme: dark,
10 variables: {colorPrimary: '#0070f2', colorBackground: '#171717'},
11 }}
12 >
13 <html lang="en">
14 <body>{children}</body>
15 </html>
16 </ClerkProvider>
17 )
18}

Conditionally Render SignedIn and SignedOut Components

Import the following components to components/nav.tsx:

components/nav.tsx
1import {SignUpButton, SignedIn, SignedOut, UserButton, SignInButton} from '@clerk/nextjs'
  • SignUpButton is a component that, when clicked, redirects to user the sign-up page.
  • SignInButton is a component that, when clicked, redirects the user to sign-in page.
  • SignedIn is a wrapper component that only renders its children if the user is signed in.
  • SignedOut is a wrapper component that only renders its children if the user is not signed in.
  • UserButton is a component that displays the current user's information and provides options to sign out or manage the account.

Now, you can update the components/nav.tsx file to conditionally render the UserButton, SignInButton, and SignUpButton components.

components/nav.tsx
1import {SignUpButton, SignedIn, SignedOut, UserButton, SignInButton} from '@clerk/nextjs'
2
3import {UserButton} from '@clerk/nextjs'
4import Link from 'next/link'
5
6
7export default function Navigation({ children }) {
8 return (
9 <>
10 <nav>
11 <ul>
12 <li>
13 <Link href="/">
14 Home
15 </Link>
16 </li>
17 <li>
18 <Link href="/courses">
19 Courses
20 </Link>
21 </li>
22
23 <SignedIn>
24 <li>
25 <UserButton afterSignOutUrl="/" />
26 </li>
27 </SignedIn>
28
29 <SignedOut>
30 <li>
31 <SignInButton>Sign in</SignInButton>
32 </li>
33 <li>
34 <SignUpButton>Sign up</SignUpButton>
35 </li>
36 </SignedOut>
37 </ul>
38 </nav>
39 </>
40 )
41}

This is exactly what we want for a full authentication experience. If a user is signed in, they will see the UserButton. If they are not signed in, they will see the SignInButton and SignUpButton.

You can also use the SignedIn and SignedOut components to conditionally display content based on the user's authentication status.

courses/[course]/[lesson]/lesson-template.tsx
1import {SignedIn, SignedOut} from '@clerk/nextjs'
2
3export default function LessonTemplate({children}: {children: React.ReactNode}) {
4 return (
5 <main>
6 <SignedIn>
7 <video
8 className="absolute inset-0 h-full w-full object-cover"
9 height={360}
10 width={640}
11 controls
12 >
13 <source src="https://example.com/path-to-your-video.mp4" type="video/mp4" />
14 Your browser does not support the video tag.
15 </video>
16 </SignedIn>
17
18 <SignedOut>Create an account to see this video.</SignedOut>
19 </main>
20 )
21}

So, if a user is signed in, they will see the video. If they are not signed in, they will see the message prompting them to create an account.

That's it! This should give you a good idea of how to integrate Clerk into your Next.js App Router project.

Conclusion

Clerk is the best third party auth solution I have ever seen. Saving me months of work and allowing me to focus on building the core features of my application. I hope this article has helped you understand how to integrate Clerk into your Next.js App Router project. If you have any questions, please feel free to reach out to me on Twitter.

Lauro Silva
Written by Lauro Silva

Lauro is a software developer and educator who loves shipping great products and creating accessible educational content for developers. Currently, they are teaching React, TypeScript, and full-stack development with Next.js.