Authentication is a process through which users verify their identity before accessing a system. In web applications, authentication is primarily implemented with email/username and password. The auth process has been further simplified with Google authentication, which enables users to sign up or sign in to a web application with their Gmail account. Google's auth approach has proven useful in many applications, especially for users who frequently forget their credentials. The limitation of Google auth is that you can only sign in or sign up with your Gmail account and nothing else. However, Clerk provides more than one option to log in or sign up, even including Google auth as one of its several options.
Clerk is a powerful authentication tool for your Next application that provides more than one way to authenticate your users. In this article, you will learn how to create a simple auth system for your Next app with Clerk.
What is Clerk?
Clerk is an authentication solution for your Next application. It is a suite of embeddable UIs and flexible APIs created to make your app’s authentication process seamless. It features customizable SignIn and SignUp components, multifactor authentication, advanced security, session management, social sign-on, email and SMS OTPs, webhooks, and the recent magic link auth. Compared to other auth solutions, Clerk tops the game as it is an all-in-one auth solution for your application while others focus on only a single auth system. Also, it is not limited to Next applications. Clerk has SDKs for modern mobile and web frameworks like React, Remix, Astro, Expo, and iOS.
Setting up Clerk
Into the course of the article, create a Next app, but before you begin, mark out the prerequisites:
- A basic understanding of React/Next
- A Clerk account.
Although you will integrate Clerk in a Next app, you only need a basic understanding of React to get your app working. You won’t be getting into the intricacies of a Next app.
Step 1: Creating a Next app
- Create a new folder named
clerk-demo
on your machine, drag it into your code editor, and open the terminal. - In the terminal, run this command
npx create-next-app@latest .
The “.” at the end allows the next app to be created in the already opened folder,clerk-demo
, in your code editor. - Run
npm run dev
to start your newly created Next app in development mode and open the app on your browser using the provided domain. It’s usuallylocalhost:3000
Step 2: Setting up your Clerk account
- Go to clerk.com and sign up with your GitHub or Google account.
- After signing up, you’ll be redirected to a page to create a new application.
- Create a new application, give it a name,
clerk-demo
, and select your preferred auth options. There are about 15 or more auth options to choose from. Clerk allows you to see the UI of your selected auth options in real-time to help your decision-making. - After selecting the auth options and clicking the Create Application button, you’ll be redirected to the app’s dashboard.
- Select your framework (NextJS) and you’ll see information on integrating Clerk.
- Copy the installing command and run it in your code editor’s terminal,
npm install @clerk/nextjs
- Copy the environment variables
- Create a
.env.local
file in the root of your Next app’s folder and paste the environment variables - Copy the
middleware.ts
code, create a file with the name, and paste it there. - Finally, update your
layout.tsx
file in theapp
folder of your application with the code from the Clerk dashboard. TheClerkProvider
wrapper enables your app to access the Clerk authentication APIs and use the Clerk components.
Now, that you’ve successfully set up Clerk, you may proceed to the Clerk’s authentication implementation phase.
Implementing Authentication
Implementing authentication with Clerk is easy. There are no complex configurations to be done except to use the ready-made components in the right places.
Step 1: Defining your Sign-in and Sign-up routes
Clerk, by default, redirects your users to another link to sign in but if you want to displace that behaviour with your predefined sign-in and sign-up routes, open your .env.local
file again and add these variables:
/* Already existing variables */
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
Step 2: Positioning the Clerk Auth Components
If you remember, you copied the layout.tsx
page from your Clerk dashboard and updated your app’s layout.tsx
file with it. The <SignedIn />
, <SignedOut />
, <SignInButton />
, and <UserButton />
components should be moved from the layout file to a <Navbar />
component which you will create in your app.
- In the root folder of your Next app, create a
/components
folder, and create aNavbar.tsx
file in it.
/components/Navbar.tsx
export default function Navbar() {
return (
<nav>
<SignedOut>
<SignInButton />
</SignedOut>
<SignedIn>
<UserButton />
</SignedIn>
</nav>
)
}
- Then, import the
Navbar.tsx
file into yourlayout.tsx
file and render the component between thebody
element, right about thechildren
prop. In this way, all the children elements will have it right above them.
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import { ClerkProvider } from "@clerk/nextjs";
import Navbar from "@/components/Navabr";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Miles Rentals",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<ClerkProvider>
<html lang="en" suppressHydrationWarning>
<body className={`${inter.className} noScrollBar`}>
<Navbar />
{children}
</body>
</html>
</ClerkProvider>
);
}
Step 3: Create the Sign-in and Sign-up pages
- For the SignIn and SignUp pages, create a
/sign-in/[[…sign-in]]
and a/sign-up/[[…sign-in]]
folder inside the/app
folder of your Next app, create a newpage.tsx
file in each, and paste this code in them.
In this way, users can navigate to /sign-in or /sign-up to log in or sign up, respectively.
/sign-in/[[...sign-in]]/page.tsx
import { SignIn } from "@clerk/nextjs";
import React from "react";
const page = () => {
return (
<main className="auth-page">
<SignIn />
</main>
);
};
export default page;
// replace <SignIn /> with <SignUp /> for the sign-up page.tsx file
// NB: [[...sign-in]] is a folder within the sign-in folder where the sign-in page.tsx file is hosted.
//Ensure the folder is named with the square brackets and the "..."
With these few lines of code, you have successfully implemented Clerk authentication for your application. Now, explore other authorization concepts you can implement in your application with Clerk.
Managing User Sessions
Clerk has several hooks that give you access to the Clerk object. Among these hooks is the useSession()
hook. The useSession()
hook manages access to the current user’s session object and other helpers for setting the active session. The helpers are isLoaded
, isSignedIn
, and session
.
import { useSession } from '@clerk/clerk-react'
const { isLoaded, session, isSignedIn } = useSession()
isLoaded
is a boolean that checks if Clerk is successfully loaded in your app. This boolean is particularly useful when you want to use any of the numerous Clerk methods in your app. To prevent errors, you can check if Clerk is loaded before using any of its methods and objects. The isSignedIn
boolean checks if a user is signed in or not. As you’ll see in the next section, this boolean can be useful in creating protected routes. The last helper in the hook is the session
object. This object lets you access important information about the currently active section. For instance, you can help the users track the time spent during each session.
import { useSession } from '@clerk/clerk-react'
export default function Home() {
const { session } = useSession()
return (
<div>
<p>This session has been active since {session.lastActiveAt.toLocaleString()}
</p>
</div>
)
}
Other useful information you can get from the session object includes user
, publicUserData
, status
, lastActiveToken
, createdAt
, updatedAt
, expireAt
, actor
, end()
, touch()
, getToken()
, and many others. See the Clerk session doc for a comprehensive list.
Protecting Routes
Protected routes are routes in an application where access is restricted to authenticated users only. In your application, you can expose some routes like the landing page, about page, and contact us page; and restrict some to authenticated users only like the user dashboard page or the settings page. Clerk has useful hooks that make it possible to create Protected routes for your application, including the useUser()
hook.
The useUser()
hook gives access to the current user’s user
object and some helpers. It returns isSignedIn
and isLoaded
booleans, like the useSession
hook, and a user
object. With the information provided by this hook, you can create a ProtectedRoute
wrapper around some pages of your application and prevent unauthorized access.
// ProtectedRoute.tsx
import { useUser } from "@clerk/clerk-react";
import { redirect } from "next/navigation";
export default function ProtectedRoute({
children,
}: {
children: React.ReactNode;
}) {
const { isSignedIn, isLoaded } = useUser();
if (!isLoaded) {
return <LoadingComponent />;
}
if (!isSignedIn) {
redirect(`/sign-in`);
}
return <>{children}</>;
}
You can then use this ProtectedRoute component to wrap any page that requires authentication:
// UserDashboard.tsx
import ProtectedRoute from "@/components/custom-routes/ProtectedRoute";
export default function UserDashboard() {
return <ProtectedRoute>{/* Your User Dashboard component */}</ProtectedRoute>;
}
Role-Based Access Control (RBAC)
In addition to basic authentication, you might want to implement Role-Based Access Control to restrict access based on user roles. Clerk provides a way to implement RBAC using user metadata. Here's how you can extend your protected routes to include role-based access:
- Configure the session token: Add user metadata to the session token in the Clerk Dashboard under Sessions > Customize session token.
- Create a TypeScript definition for roles: Define your roles in a globals.d.ts file.
- Create a helper function to check roles
import { Roles } from '@/types/global'
import { auth } from '@clerk/nextjs/server'
export const checkRole = (role: Roles) => {
const { sessionClaims } = auth()
return sessionClaims?.metadata.role === role
}
- Extend your ProtectedRoute component to include role checks
import { useUser } from "@clerk/clerk-react";
import { redirect } from "next/navigation";
import { checkRole } from "@/utils/roles";
export default function ProtectedRoute({
children,
requiredRole,
}: {
children: React.ReactNode;
requiredRole?: Roles;
}) {
const { isSignedIn, isLoaded } = useUser();
if (!isLoaded) {
return <LoadingComponent />;
}
if (!isSignedIn) {
redirect(`/sign-in`);
}
if (requiredRole && !checkRole(requiredRole)) {
redirect(`/unauthorized`);
}
return <>{children}</>;
}
Now you can protect routes based on both authentication and roles
import ProtectedRoute from "@/components/custom-routes/ProtectedRoute";
export default function AdminDashboard() {
return (
<ProtectedRoute requiredRole="admin">
{/* Your Admin Dashboard component */}
</ProtectedRoute>
);
}
This implementation allows you to create a flexible system for protecting routes based on both authentication status and user roles, enhancing the security of your application.
Customization Option
Clerk provides many customization options for your application. You can customize the SignIn
and SignUp
component to use your logo rather than Clerk’s logo.
- Navigate to your Clerk dashboard, right-click on your app’s name, and click the settings icon. There, you’ll see different customization settings to make Clerk feel more like yours.
Besides the logo change on the SignIn
and SignUp
components, you can also customize the theme of all Clerk components to match your application’s theme at once or individually. Clerk provides an appearance
prop in the ClerkProvider
wrapper component, which you use in your layout.tsx
file. You can also use the appearance
prop in any Clerk component to customize it individually. The appearance
prop accepts the following properties:
-
baseTheme
: The base theme to be used in your component. See the themes documentation for available themes. -
variables
: An object to define the styles like color, font size, font weight, and much more. -
layout
: This property is particularly useful for customizations that are hard to implement with CSS. It gives you access to configuration options that affect the layout of Clerk components. See layout for more information. -
elements
: If you want to override the default style of a specific element, this property is for you. For more information, see elements.
All of these properties are optional so you can use 1 or 2 or all of them, independently. The code sample below illustrates a sample use of the appearance
prop in the ClerkProvider
wrapper component.
import type { Metadata } from "next";
import { ClerkProvider } from "@clerk/nextjs";
import { dark } from "@clerk/themes";
import localFont from "next/font/local";
import "./globals.css";
import { cn } from "@/lib/utils";
const geistSans = localFont({
src: "./fonts/GeistVF.woff",
variable: "--font-geist-sans",
weight: "100 900",
});
const geistMono = localFont({
src: "./fonts/GeistMonoVF.woff",
variable: "--font-geist-mono",
weight: "100 900",
});
export const metadata: Metadata = {
title: "Clerk Auth",
description: "Code sample of using the appearance prop",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<ClerkProvider
appearance={{
baseTheme: dark,
variables: {
colorPrimary: "#3371FF",
fontSize: "16px",
},
}}
>
<html lang="en" suppressHydrationWarning>
<body
className={cn(
"antialiased min-h-screen",
geistSans.variable,
geistMono.variable
)}
>
{children}
</body>
</html>
</ClerkProvider>
);
}
Conclusion
In this article, you learned how to implement a basic but strong authentication system in your Next.js application using Clerk. You saw how Clerk can provide an all-in-one solution which is both robust and very easy to implement. You started by creating a Clerk account and linking it with the Next.js application. Then you integrated the primary authentication functionality: sign-in and sign-up. Later, you dove deeper into more advanced topics, like user session management, protecting routes, and even Role-Based Access Control. You learned how Clerk's customizability allowed you to tailor the authentication experience to your application. You have seen how easily you can implement different themes and layouts, making every adjustment necessary to fit your app's design.
With web application development, you will need a secure, customizable, full-featured authentication system—that's what Clerk offers with much simplicity in implementation hence highly recommended for your Next.js projects. You now know how to effectively use Clerk's authentication solution for your projects, whether small or large scalable applications. Whether you're building a small project or a large-scale application, you now have the knowledge to use Clerk's authentication solution effectively. You're equipped with the tools and flexibility to meet your authentication needs efficiently and securely in your Next.js applications.