Get started w/ Next.js

Learn to install and initialize Clerk in a new Next.js application.

1. Create a new application

i. Run create command

Start by creating a new Next.js application.

npx create-next-app
# or
yarn create next-app

For more information about these commands, please reference the Next.js documentation.

ii. Install clerk-react

Install Clerk's NPM package for React applications.

# From your application's root directory
cd yourapp
npm install @clerk/clerk-react
# or
yarn add @clerk/clerk-react

iii. Add environment variables

Create a file named .env.local in your application root. Any variables inside this file with the NEXT_PUBLIC prefix will be accessible in your frontend via process.env.NEXT_PUBLIC_X.

Make sure you update these variables with the Frontend API found in your dashboard.

NEXT_PUBLIC_CLERK_FRONTEND_API=clerk.abc123.lcl.dev

iii. Start the dev server

npm run dev
# or
yarn dev

2. Configure pages/_app.js

Clerk requires your application to be wrapped in the <ClerkProvider> context. In Next.js, we add this in pages/_app.js.

In the code below, we've also added a sign in requirement for every page, unless the page is listed in the publicPages variable.

This is just one example of how authentication can be handled with Clerk. Feel free to adjust the strategy to better serve your use case!

pages/_app.js
import "../styles/globals.css";
import {
ClerkProvider,
RedirectToSignIn,
SignedIn,
SignedOut
} from "@clerk/clerk-react";
import { useRouter } from "next/router";
// Retrieve Clerk settings from the environment
const clerkFrontendApi = process.env.NEXT_PUBLIC_CLERK_FRONTEND_API;
/**
* List pages you want to be publicly accessible, or leave empty if
* every page requires authentication. Use this naming strategy:
* "/" for pages/index.js
* "/foo" for pages/foo/index.js
* "/foo/bar" for pages/foo/bar.js
* "/foo/[...bar]" for pages/foo/[...bar].js
*/
const publicPages = [];
function MyApp({ Component, pageProps }) {
const router = useRouter();
/**
* If the current route is listed as public, render it directly.
* Otherwise, use Clerk to require authentication.
*/
return (
<ClerkProvider
frontendApi={clerkFrontendApi}
navigate={(to) => router.push(to)}
>
{publicPages.includes(router.pathname) ? (
<Component {...pageProps} />
) : (
<>
<SignedIn>
<Component {...pageProps} />
</SignedIn>
<SignedOut>
<RedirectToSignIn />
</SignedOut>
</>
)}
</ClerkProvider>
);
}
export default MyApp;

3. Hello, world!

That's all you need to configure with Clerk. Now you can say hello to your user:

pages/index.js
import { UserButton, useUser } from "@clerk/clerk-react";
export default function Home() {
const { firstName } = useUser();
return (
<>
<header>
<UserButton />
</header>
<main>Hello, {firstName}!</main>
</>
);
}

Visit https://localhost:3000 to see your page. If you haven't signed in yet, you will be redirected to the sign in page.

4. Make a backend request

i. Add a fetcher utility

We've documented two strategies in the tabs below, but you can use any strategy that meets the request requirements for making backend requests.

swr
react-query
swr

Install swr

npm install swr
# or
yarn add swr

Restart the application

npm run dev
# or
yarn dev

Create utils/fetchers.js

Here, we wrap useSWR to add the active session ID to the request's query parameters. This way, one user will never retrieve data from another user's cache.

utils/fetchers.js
import { useClerk } from "@clerk/clerk-react";
import useSWR from "swr";
export const useClerkSWR = (url, fetcher = null) => {
const { session } = useClerk();
if (!session) {
throw new Error("Cannot useClerkSWR when there is no session.");
}
const sessionId = session.id;
// The fetcher is not included as part of useSWR's cache key,
// so we must append clerk session ID directly to the URL
const urlWithSession = new URL(url, window.location.href);
urlWithSession.searchParams.set("_clerk_session_id", sessionId);
// The default fetcher includes credentials and returns json
if (!fetcher) {
fetcher = (request, options) => {
return fetch(request, { ...options, credentials: "include" }).then((r) =>
r.json()
);
};
}
return useSWR(urlWithSession, fetcher);
};
react-query

Install swr

npm install react-query
# or
yarn add react-query

Restart the application

npm run dev
# or
yarn dev

Create utils/fetchers.js

Here we create a <ClerkQueryClientProvider> that switches query clients depending on the active session. This way, one user will never retrieve data from another user's cache.

utils/fetchers.js
import { QueryClient, QueryClientProvider } from "react-query";
import { useContext } from "react";
import { ClerkContext } from "@clerk/clerk-react";
const clerkQueryClients = {};
// Use the queryKey as the URL and append _clerk_session_id
const clerkQueryFn = (sessionId) => {
return ({ queryKey }) => {
const urlWithSession = new URL(queryKey, window.location.href);
urlWithSession.searchParams.set("_clerk_session_id", sessionId);
return fetch(urlWithSession, { credentials: "include" }).then((r) =>
r.json()
);
};
};
export const ClerkQueryClientProvider = ({ children }) => {
const { clerk } = useContext(ClerkContext);
if (!clerk.session) {
return children;
}
const { session } = clerk;
if (!(session.id in clerkQueryClients)) {
clerkQueryClients[session.id] = new QueryClient({
defaultOptions: {
queries: {
queryFn: clerkQueryFn(session.id),
},
},
});
}
return (
<QueryClientProvider client={clerkQueryClients[session.id]}>
{children}
</QueryClientProvider>
);
};

Update pages/_app.js

First, add an import for the newly-created ClerkQueryCleintProvider to the top of the file.

pages/_app.js
import { ClerkQueryClientProvider } from "../utils/fetchers";

Then, add <ClerkQueryCleintProvider> to MyApp's return statement. You should nest it directly inside <ClerkProvider>:

pages/_app.js
<ClerkProvider
frontendApi={clerkFrontendApi}
navigate={(to) => router.push(to)}
>
<ClerkQueryClientProvider>
// ...
</ClerkQueryClientProvider>
</ClerkProvider>

ii. Add a backend request

swr
react-query
swr

Update pages/index.js to add the backend request. We've configured this to request /api/hello, since that route is predefined in Next.js.

pages/index.js
import { UserButton, useUser } from "@clerk/clerk-react";
import { useClerkSWR } from "../utils/fetchers";
export default function Home() {
const { firstName } = useUser();
const { data } = useClerkSWR("/api/hello");
return (
<>
<header>
<UserButton />
</header>
<main>
<div>Hello, {firstName}!</div>
<div>Backend data: {JSON.stringify(data)}</div>
</main>
</>
);
}

Visit https://localhost:3000 to see your page. Next.js apps come with /api/hello preconfigured, so you should see the following:

Backend data: {"name":"John Doe"}

react-query

Update pages/index.js to add the backend request. We've configured this to request /api/hello, since that route is predefined in Next.js.

pages/index.js
import { UserButton, useUser } from "@clerk/clerk-react";
import { useQuery } from "react-query";
export default function Home() {
const { firstName } = useUser();
const { data } = useQuery("/api/hello");
return (
<>
<header>
<UserButton />
</header>
<main>
<div>Hello, {firstName}!</div>
<div>Backend data: {JSON.stringify(data)}</div>
</main>
</>
);
}

Visit https://localhost:3000 to see your page. Next.js apps come with /api/hello preconfigured, so you should see the following:

Backend data: {"name":"John Doe"}

iii. Add Clerk to your backend

The final step is to update /api/hello to add authentication to the request. For help with this, you can follow our backend documentation for authenticating Next.js API routes.

5. Mount components (optional)

By default, Clerk hosts the SignIn, SignUp, and UserProfile components on the accounts.* subdomain of your root domain.

If you prefer, you can also "mount" these components directly in your application.

i. Mount SignIn

Create pages/sign-in/[[...index]].js. This uses an optional catch-all route so the page will be used for /sign-in and all of its subpaths.

import { SignIn } from "@clerk/clerk-react";
export default function SignInPage() {
return <SignIn path="/sign-in" routing="path" />;
}

ii. Mount SignUp

Create pages/sign-up/[[...index]].js. This uses an optional catch-all route so the page will be used for /sign-up and all of its subpaths.

import { SignUp } from "@clerk/clerk-react";
export default function SignUpPage() {
return <SignUp path="/sign-up" routing="path" />;
}

iii. Mount UserProfile

Create pages/user/[[...index]].js. This uses an optional catch-all route so the page will be used for /user and all of its subpaths.

import { UserProfile } from "@clerk/clerk-react";
export default function UserProfilePage() {
return <UserProfile path="/user" routing="path" />;
}

iv. Update pages/_app.js

This is the same logic as the first configuration, but with one additional change:

Set the new SignIn and SignUp pages as publicPages.

You will need to re-update clerkFrontendApi

If you used react-query to make a backend request, make sure you re-add <ClerkQueryProviderComponent> inside of the ClerkProvider.

pages/_app.js
import "../styles/globals.css";
import {
ClerkProvider,
RedirectToSignIn,
SignedIn,
SignedOut
} from "@clerk/clerk-react";
import { useRouter } from "next/router";
import { useEffect } from "react";
// Retrieve Clerk settings from the environment
const clerkFrontendApi = process.env.NEXT_PUBLIC_CLERK_FRONTEND_API;
/**
* List pages you want to be publicly accessible, or leave empty if
* every page requires authentication. Use this naming strategy:
* "/" for pages/index.js
* "/foo" for pages/foo/index.js
* "/foo/bar" for pages/foo/bar.js
* "/foo/[...bar]" for pages/foo/[...bar].js
*/
const publicPages = ["/sign-in/[[...index]]", "/sign-up/[[...index]]"];
function MyApp({ Component, pageProps }) {
const router = useRouter();
/**
* If the current route is listed as public, render it directly.
* Otherwise, use Clerk to require authentication.
*/
return (
<ClerkProvider
frontendApi={clerkFrontendApi}
navigate={(to) => router.push(to)}
>
{publicPages.includes(router.pathname) ? (
<Component {...pageProps} />
) : (
<>
<SignedIn>
<Component {...pageProps} />
</SignedIn>
<SignedOut>
<RedirectToSignIn />
</SignedOut>
</>
)}
</ClerkProvider>
);
}
export default MyApp;

v. Update instance settings

The final step is to update your instance settings so that users will be defaulted to your mounted components instead of the accounts.* subdomain.

To change the settings, open your instance in the Dashboard and navigate to Settings URLs and Redirects.