Use Clerk with Remix
Learn how to use Clerk to quickly and easily add secure authentication and user management to your Remix application.
Install @clerk/remix
Once you have a Remix application ready, you need to install Clerk's Remix SDK. This gives you access to our prebuilt components and hooks for Remix applications.
terminalnpm install @clerk/remix
terminalyarn add @clerk/remix
terminalpnpm i @clerk/remix
Set environment keys
Below is an example of an .env file. If you are signed into your Clerk Dashboard, your keys should become visible by clicking on the eye icon.
.envCLERK_PUBLISHABLE_KEY={{pub_key}} CLERK_SECRET_KEY={{secret}}
Configure rootAuthLoader
To configure Clerk in your Remix application, you will need to update your root loader. This will enable you to have access to authentication state in any Remix routes.
app.tsximport type { MetaFunction,LoaderFunction } from "@remix-run/node"; import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration, } from "@remix-run/react"; import { rootAuthLoader } from "@clerk/remix/ssr.server"; export const meta: MetaFunction = () => ({ charset: "utf-8", title: "New Remix App", viewport: "width=device-width,initial-scale=1", }); export const loader: LoaderFunction = (args) => rootAuthLoader(args); export default function App() { return ( <html lang="en"> <head> <Meta /> <Links /> </head> <body> <Outlet /> <ScrollRestoration /> <Scripts /> <LiveReload /> </body> </html> ); }
If you need to load in additonal data, you can pass your loader directly to the rootAuthLoader
.
app/root.tsx//Imports export const loader: LoaderFunction = args => { return rootAuthLoader(args, ({ request }) => { const { sessionId, userId, getToken } = request.auth; // fetch data return { yourData: 'here' }; }); }; // Additonal application code.
Configure ClerkApp
Clerk provides a ClerkApp
wrapper to provide the authentication state to your React tree. This helper works with Remix SSR out-of-the-box and follows the "higher-order component" paradigm.
app/root.tsximport type { MetaFunction,LoaderFunction } from "@remix-run/node"; import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration, } from "@remix-run/react"; import { rootAuthLoader } from "@clerk/remix/ssr.server"; // Import ClerkApp import { ClerkApp } from "@clerk/remix"; export const meta: MetaFunction = () => ({ charset: "utf-8", title: "New Remix App", viewport: "width=device-width,initial-scale=1", }); export const loader: LoaderFunction = (args) => rootAuthLoader(args); function App() { return ( <html lang="en"> <head> <Meta /> <Links /> </head> <body> <Outlet /> <ScrollRestoration /> <Scripts /> <LiveReload /> </body> </html> ); } // Wrap your app in ClerkApp(app) export default ClerkApp(App);
Set ClerkCatchBoundary
Clerk uses short lived tokens to keep your application secure. To refresh expired tokens, Clerk uses Remix's catch boundary.
If you are seeing a 401 after a short period of time, even though your user is logged in, please verify you have implemented this section.
app/root.tsximport type { MetaFunction,LoaderFunction } from "@remix-run/node"; import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration, } from "@remix-run/react"; import { rootAuthLoader } from "@clerk/remix/ssr.server"; import { ClerkApp, ClerkCatchBoundary } from "@clerk/remix"; export const meta: MetaFunction = () => ({ charset: "utf-8", title: "New Remix App", viewport: "width=device-width,initial-scale=1", }); export const loader: LoaderFunction = (args) => rootAuthLoader(args); // add a Catch Boundary export const CatchBoundary = ClerkCatchBoundary(); function App() { return ( <html lang="en"> <head> <Meta /> <Links /> </head> <body> <Outlet /> <ScrollRestoration /> <Scripts /> <LiveReload /> </body> </html> ); } export default ClerkApp(App);
If you need to add a custom boundary to your application, you can pass it as an argument to the ClerkCatchBoundary
.
app/root.tsx// imports export const CatchBoundary = ClerkCatchBoundary(YourBoundary); // additional application code
Clerk offers a set of prebuilt components that you can use to embed sign in, sign up, and other user management functions into your Next.js application. This example uses the <SignIn/>
and <SignUp />
components by utlizing Remix routes.
The functionality of the components are controlled by the instance settings you specify in your Clerk Dashboard.
Build your sign up
app/routes/sign-up.$.tsximport { SignUp } from "@clerk/remix"; export default function SignUpPage() { return ( <div> <h1>Sign Up route</h1> <SignUp /> </div> ); }
Build your sign in
app/routes/sign-in.$.tsximport { SignIn } from "@clerk/remix"; export default function SignInPage() { return ( <div> <h1>Sign In route</h1> <SignIn /> </div> ); }
Update your environment variables
Next, add environment variables for the signIn
, signUp
, afterSignUp
, and afterSignIn
paths:
.envCLERK_SIGN_IN_URL=/sign-in CLERK_SIGN_UP_URL=/sign-up CLERK_AFTER_SIGN_IN_URL=/ CLERK_AFTER_SIGN_UP_URL=/
These values control the behavior of the components when you sign in or sign up and when you click on the respective links at the bottom of each component.
Update your dashboard settings
To make sure you are using the embedded components, you will need to update your instance settings in the Clerk Dashboard to use the embedded components.

By default, the Clerk components inherit the font family.
Protecting your pages
Client side
Clerk offers Control Components that allow you to protect your pages. Below is a simple example using Clerk's hosted components and the <SignedIn/>
and <SignedOut/>
control components.
routes/index.tsximport { SignedIn, SignedOut, RedirectToSignIn, UserButton, } from "@clerk/remix"; export default function Index() { return ( <div> <SignedIn> <h1>Index route</h1> <p>You are signed in!</p> <UserButton /> </SignedIn> <SignedOut> <RedirectToSignIn /> </SignedOut> </div> ); }
Server side
To protect your routes, you can use the the loader to check for the userId
singleton. If it doesn't exist, redirect your user back to the sign in page.
routes/index.tsximport { UserButton } from "@clerk/remix"; import { getAuth } from "@clerk/remix/ssr.server"; import { LoaderFunction, redirect } from "@remix-run/node"; export const loader: LoaderFunction = async (args) => { const { userId } = await getAuth(args); if (!userId) { return redirect("/sign-in"); } return {}; } export default function Index() { return ( <div> <h1>Index route</h1> <p>You are signed in!</p> <UserButton afterSignOutUrl="/"/> </div> ); }
Read session and user data
Clerk provides a set of hooks and helpers that you can use to access the active session and user data in your Remix application. Here are examples of how to use these helpers.
Client side
useAuth()
The useAuth
hook is a convenient way to access the current auth state. This hook provides the minimal information needed for data-loading and helper methods to manage the current active session.
routes/example.tsximport { useAuth } from "@clerk/remix"; export default function Example() { const { isLoaded, userId, sessionId, getToken } = useAuth(); // In case the user signs out while on the page. if (!isLoaded || !userId) { return null; } return ( <div> Hello, {userId} your current active session is {sessionId} </div> ); }
useUser()
The useUser
hook is a convenient way to access the current user data where you need it. This hook provides the user data and helper methods to manage the current active session.
routes/example.tsximport { useUser } from "@clerk/remix"; export default function Example() { const { isLoaded, isSignedIn, user } = useUser(); if (!isLoaded || !isSignedIn) { return null; } return <div>Hello, {user.firstName} welcome to Clerk</div>; }
Server side
getAuth()
The getAuth
hook allows you to retrieve auth state and also provides ways to fetch data using the getToken
function.
routes/example.tsxexport const loader: LoaderFunction = async (args) => { // Using getAuth to retrieve data as needed. const { userId, sessionId, getToken } = await getAuth(args); console.log("Use getAuth() to access the auth state:", userId, sessionId, getToken); if (!userId) { return redirect("/sign-in?redirect_url=" + args.request.url); } // fetch data where you need the userId. const posts = await mockGetPosts(userId); return { posts }; };
Retrieve full user
routes/example.tsximport { LoaderFunction, redirect } from "@remix-run/node"; import { getAuth } from "@clerk/remix/ssr.server"; import { createClerkClient } from '@clerk/remix/api.server'; export const loader: LoaderFunction = async (args) => { const { userId } = await getAuth(args); if (!userId) { return redirect("/sign-in?redirect_url=" + args.request.url); } const user = await createClerkClient({secretKey: process.env.CLERK_SECRET_KEY}).users.getUser(userId); return { serialisedUser: JSON.stringify(user) }; };
Next steps
Now that you have an application integrated with Clerk, you will want to read the following documentation: