Next.js Authentication: Add Clerk Auth to a Next.js App Router Project
Authentication is a core component of any application, as it restricts access to certain content. This restricted content could include a user dashboard with personal information, past orders, and payment details. It could also apply to a course site, where only paying users should have access to courses and resources.
Despite its importance in most applications, authentication has a reputation for being challenging to implement in web apps. However, some products and services, such as Clerk, aim to change this.
This tutorial will guide you on how to implement Clerk in a Next.js App Router project, allowing users to sign up for an account and display their username on each page. Let's get started!
Why Clerk?
Before we start building our project, letâs first take a moment to consider why Clerk. There are many authentication providers and services available for Next.js. Clerk has become increasingly popular over the last year and for good reasons:
- As weâll see in this tutorial, Clerk makes it easy to implement authentication on an application across client and server-rendered pages and components.
- Theyâve invested heavily in their Next.js integration, which makes it easy to protect Server Actions and API routes using their pre-built components, hooks, and functions.
Integrating authentication in Next.js App Router
Let's dive in and start building our project. In this case, we'll assume you're creating a blog that's only accessible to paying subscribers. Thus, we'll need to add authentication before accessing the blog.
Step 1: Setting up our project
The first thing, we need to do is to clone the base repository weâre going to be using for this tutorial. Instead of building an application from scratch, weâre going to be adding authentication to the Prismic blog starter template. (Demo:Â Open live demo)
To clone this repo, run the following commands in your terminal:
npx degit prismicio-community/nextjs-starter-prismic-blog your-project-name
cd your-project-name
npx @slicemachine/init@latest
The commands will do the following:
- Start a new Next.js project using this starter.
- Ask you to log in to Prismic or create an account.
- Create a new Prismic content repository with sample content.
When you're ready to start your project, run the following command: npm run dev
.
Once you have the project set up and running, weâre ready to start implementing authentication using Clerk.
Step 2: Configuring Clerk
We first need to configure our application on Clerk. To do this, head over to Clerkâs website and create a new account if you donât already have one.
Once youâre signed into your Clerk account, youâll need to create a new application (if youâve just signed up, you should be automatically taken to this page). When creating your application, youâll then be asked, âwhich authentication methods youâd like to useâ; select just âEmailâ and unselect all the other options.
Then, after creating your new application, youâll want to copy the environment variables displayed (make sure âNext.jsâ is selected).
Step 3: Configuring our application to add sign-up functionality
With your Clerk application now created and your environment variables copied, we can turn our attention back to the code.
- The first thing weâll want to do is to add the environment variables we copied earlier into an
.env.local
file at the root of the project. - Then with the environment variables added to the project, the next thing is to install the Next.js Clerk package, which we can do by running the command
npm install @clerk/nextjs
in your terminal. - After the install finishes, we can start setting up Clerk in our project. The first step is to wrap our application in the
ClerkProvider
component; to do this, update yoursrc/app/layout.js
file to look like the code below.
import "../styles/globals.css";
import { Inter, Libre_Baskerville } from "next/font/google";
import { PrismicPreview } from "@prismicio/next";
import { repositoryName } from "@/prismicio";
import { ClerkProvider } from "@clerk/nextjs";
const inter = Inter({
subsets: ["latin"],
display: "swap",
});
const libre_baskerville = Libre_Baskerville({
subsets: ["latin"],
style: ["normal", "italic"],
weight: ["400", "700"],
variable: "--libre-baskerville",
display: "swap",
});
export default function RootLayout({ children }) {
return (
<ClerkProvider>
<html
lang="en"
className={`${inter.className} ${libre_baskerville.className}`}
>
<body className="overflow-x-hidden antialiased">
<main>
{process.env.NODE_ENV === "development" && (
<div
style={{
background: "#5163ba",
padding: "1rem",
textAlign: "center",
fontSize: "0.85rem",
color: "#fff",
}}
>
<p>
<strong>đ Welcome to your new website!</strong> To customize
the code and content of this site,{" "}
<a
href="<https://github.com/prismicio-community/nextjs-starter-prismic-blog/tree/master/docs>"
target="_blank"
rel="noreferrer"
style={{ textDecoration: "underline" }}
>
see the documentation
</a>
. Remove this bar in <code>app/layout.js</code>.
</p>
</div>
)}
{children}
<PrismicPreview repositoryName={repositoryName} />
</main>
</body>
</html>
</ClerkProvider>
);
}
With our application now wrapped in the ClerkProvider
component, there is only one thing left for us to do to protect our application with Clerk: add a custom middleware to our project.
Step 4: Creating a custom middleware.js
To create this custom middleware, create a new file in the src
directory called middleware.js
and add the below code:
import { authMiddleware } from "@clerk/nextjs";
export default authMiddleware({});
export const config = {
matcher: ["/((?!.+\\\\.[\\\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
};
With this file added to our project, our application is officially gated by Clerk authentication! đ
At this point, you can test the authentication out by starting the local dev server. Run npm run dev
in your terminal and head over to http://localhost:3000
.
You should be immediately redirected to the Clerk sign-up page. After successfully signing up, youâll be automatically redirected to our application where you should see the blog home page.
Step 5: Adding sign-out functionality
Now, the keen-eyed among you might have noticed that there is no mechanism for a user to sign out of our application. Letâs now add this functionality!
To add the sign out functionality weâre going to use the custom UserButton
component that is included in the @clerk/nextjs
package we installed earlier.
To configure this component in our project, head over to the Header.js
file and update the Header
component to be the code below.
import { UserButton } from "@clerk/nextjs";
// ...rest of the imports and component definitions
export const Header = ({
withDivider = true,
withProfile = true,
navigation,
settings,
}) => {
return (
// Adding the UserButton component in the nav
<Bounded as="header">
<div className="grid grid-cols-1 justify-items-center gap-20">
<nav className="flex flex-row gap-4 items-center">
<UserButton />
<ul className="flex flex-wrap justify-center gap-10">
<NavItem>
<Link href="/">
<PrismicText field={navigation.data.homepageLabel} />
</Link>
</NavItem>
{navigation.data?.links.map((item) => (
<NavItem key={prismic.asText(item.label)}>
<PrismicNextLink field={item.link}>
<PrismicText field={item.label} />
</PrismicNextLink>
</NavItem>
))}
</ul>
</nav>
{withProfile && (
<Profile
name={settings.data.name}
description={settings.data.description}
profilePicture={settings.data.profilePicture}
/>
)}
{withDivider && <HorizontalDivider />}
</div>
</Bounded>
);
};
In this code, we import the UserButton
from @clerk/nextjs
then we add it to our Header
component. We also update some of the styles of the Header
component to make it display the UserButton
component a bit nicer next to the pre-existing navigation bar.
Now, with the UserButton
component added, we can go to our application in our browser and test it out. We should now be able to click on the UserButton
component next to the navigation bar and then press the âSign Outâ button and after a few seconds be redirected to the Clerk sign-in page.
This application allows users to sign up and sign in using the Clerk authentication pages, before redirecting them to our blog. Once authenticated, users can navigate and view the blog's content. They can also sign out using the UserButton
component.
While the full authentication flow is functional as is, we can enhance the user experience by hosting our own branded sign-up and sign-in pages. This eliminates the need to redirect to a Clerk URL. Let's do that next!
Stay on Top of New Tools, Frameworks, and More
Research shows that we learn better by doing. Dive into a monthly tutorial with the Optimized Dev Newsletter that helps you decide which new web dev tools are worth adding to your stack.
Step 6: Creating a custom sign-in and sign-up with Clerk
To get our custom sign-in and signup pages, we'll first need to add additional environment variables to our project. This is to tell Clerk where our signing and signup pages will be hosted in our application. Clerk will use these environment variables in their custom components SignIn
and SignUp
components.
To configure these environment variables, add the below variables at the bottom of your .env.local
file at the root of your project.
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/
With these environment variables added, weâre ready to get started with creating our custom pages.
Letâs start by creating the custom sign-in page.
Create a new file at /src/app/sign-in/[...sign-in]/page.js
and then add the below code to it.
import { SignIn } from "@clerk/nextjs";
import Link from "next/link";
export default function Page() {
return (
<div className="h-screen flex flex-col items-center justify-center gap-6 min-w-full">
<h2 className="text-2xl font-semibold text-gray-800">
Sign in to your account
</h2>
<SignIn
appearance={{
elements: { footer: "hidden", formButtonPrimary: "bg-blue-700" },
}}
/>
<div className="flex flex-row gap-1 text-sm">
<p>Not a user?</p>
<Link href="/sign-up" className="text-blue-700 underline font-semibold">
Sign up here.
</Link>
</div>
</div>
);
}
Letâs go through this code and see whatâs happening.
- We start by defining some base layouts and styles for the page as well as creating a header for it.
- We then add the
SignIn
component from the@clerk/nextjs
package. This component will handle the rendering of the sign-in form as well as the logic required to authenticate the user. At this point, we also make some customizations to the element using theappearance
prop to remove the built-in footer and change the color of the submit button. - Finally, under the
SignIn
component, we add a custom footer to the page that displays a link to our sign-up page.
Letâs do it again for our sign-up page!
Create a new file at src/app/sign-up/[...sign-up]/page.js
and add the below code to it.
import { SignUp } from "@clerk/nextjs";
import Link from "next/link";
export default function Page() {
return (
<div className="h-screen flex flex-col items-center justify-center gap-6 min-w-full">
<h2 className="text-2xl font-semibold text-gray-800">
Sign up for a new account
</h2>
<SignUp
appearance={{
elements: { footer: "hidden", formButtonPrimary: "bg-blue-700" },
}}
/>
<div className="flex flex-row gap-1 text-sm">
<p>Already a user?</p>
<Link href="/sign-in" className="text-blue-700 underline font-semibold">
Sign in here.
</Link>
</div>
</div>
);
}
The code weâve added here is very similar to the custom sign-in page we just went through, with the only difference being the use of the SignUp
component from @clerk/nextjs
instead of SignIn
. We also include a link to the sign-in page in the footer of the component instead of a link to the sign-up page, as we did earlier.
Last step: updating the middleware.js
Weâre almost ready to test our new custom authentication pages, but before we can do that, we need to update our middleware.js
file. In our middleware.js
file, we need to allow the /sign-in
and /sign-up
pages to be public routes, which means they can be accessed by users who arenât authenticated.
To configure this, update your middleware.js
file to use the code below.
import { authMiddleware } from "@clerk/nextjs";
export default authMiddleware({
publicRoutes: ["/sign-in", "/sign-up"],
});
export const config = {
matcher: ["/((?!.+\\\\.[\\\\w]+$|_next).*)", "/", "/(api|trpc)(.*)"],
};
This file is identical to the one we added earlier, with the only difference being the addition of the publicRoutes
property inside the authMiddleware
function.
The second thing we need to do is to update our UserButton
component in the Header.js
file to redirect to our custom sign-in page after the user presses âSign Out.â We can do this by updating the UserButton
code from earlier to look like this <UserButton afterSignOutUrl="/sign-in" />
.
import { UserButton } from "@clerk/nextjs";
// ...rest of the imports and component definitions
export const Header = ({
withDivider = true,
withProfile = true,
navigation,
settings,
}) => {
return (
// Notice the change in the <UserButton/> component
<Bounded as="header">
<div className="grid grid-cols-1 justify-items-center gap-20">
<nav className="flex flex-row gap-4 items-center">
<UserButton afterSignOutUrl="/sign-in" />
<ul className="flex flex-wrap justify-center gap-10">
<NavItem>
<Link href="/">
<PrismicText field={navigation.data.homepageLabel} />
</Link>
</NavItem>
{navigation.data?.links.map((item) => (
<NavItem key={prismic.asText(item.label)}>
<PrismicNextLink field={item.link}>
<PrismicText field={item.label} />
</PrismicNextLink>
</NavItem>
))}
</ul>
</nav>
{withProfile && (
<Profile
name={settings.data.name}
description={settings.data.description}
profilePicture={settings.data.profilePicture}
/>
)}
{withDivider && <HorizontalDivider />}
</div>
</Bounded>
);
};
Testing our new authentication
With our custom authentication pages now added and configured, weâre ready to test our finished example application.
To do this, start up your local dev server again (if you stopped it) using npm run dev
and then head over to http://localhost:3000
. Then, if youâre not already authenticated, you should be automatically redirected to our custom sign-in page at http://localhost:3000/sign-in
where you can sign in or navigate to the custom sign-up page at http://localhost:3000/sign-up
using the link we added.
Then, once authenticated, you should be able to access the blog home page.
Once youâre finished, you should be able to sign out of the application using the UserButton
component we added and be redirected back to the sign-in page.
In summary, your completed application should look something like the one below.
Closing Thoughts
If your application looks and functions like the one shown in the video and you can perform all the actions described, then congratulations, youâve made an application gated with Clerk authentication! đ
At this point, you may be wondering what your next steps would be. One option would be to look at making completely custom authentication pages where you use no pre-built components from Clerk. Another option would be to implement a social auth provider like Google or GitHub. Or, maybe youâll want to learn more about using Prismic and Next.js and enrich your new blog with the Prismic Academy.
If youâre interested in checking out the complete code of this project, you can see it on GitHub.
Thank you for reading.
Further learning
Build a full Next.js website with Next.js, Tailwind CSS, Prismic, and TypeScript!
If you're looking to take your skills to the next level, try this Next.js full website tutorial course! See the power of Prismic as a Headless website builder alongside Next.js, the popular React framework. In this tutorial, you'll create a trendy, dark, and modern website and use GSAP to add wonderful animations, both on page load and on scroll. You'll also use Tailwind CSS to style the super-polished website.