Docs

Use Clerk with Remix

Learn how to use Clerk to quickly and easily add secure authentication and user management to your Remix application. This guide assumes that you are using Remix v2 or later.

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.

terminal
npm install @clerk/remix
terminal
yarn add @clerk/remix
terminal
pnpm add @clerk/remix

Set environment variables

Below is an example of an .env file.

Pro tip! If you are signed into your Clerk Dashboard, your secret key should become visible by clicking on the eye icon. Otherwise, you can find your keys in the Clerk Dashboard on the API Keys page.

.env
CLERK_PUBLISHABLE_KEY=YOUR_PUBLISHABLE_KEY
CLERK_SECRET_KEY=YOUR_SECRET_KEY

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/root.tsx
import type { MetaFunction, LoaderFunction } from "@remix-run/node";
import {
  Links,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
} from "@remix-run/react";

// Import rootAuthLoader
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 as the root route loader
export const loader: LoaderFunction = (args) => rootAuthLoader(args);

export function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <head>
        <Meta />
        <Links />
      </head>
      <body>
        {children}
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

export default function App() {
  return <Outlet />;
}

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.tsx
import type { MetaFunction, LoaderFunction } from "@remix-run/node";

import {
  Links,
  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);

export function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <head>
        <Meta />
        <Links />
      </head>
      <body>
        {children}
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}

function App() {
  return <Outlet />;
}

export default ClerkApp(App);

Build your own sign-in and sign-up pages

In addition to the Account Portal pages, Clerk also offers a set of prebuilt components that you can use instead to embed sign-in, sign-up, and other user management functions into your Remix application. We are going to use the <SignIn /> and <SignUp /> components by utilizing the Remix optional catch-all route.

The functionality of the components are controlled by the instance settings you specify in your Clerk Dashboard.

Build your sign-up page

app/routes/sign-up.$.tsx
import { SignUp } from "@clerk/remix";

export default function SignUpPage() {
  return (
    <div>
      <h1>Sign Up route</h1>
      <SignUp />
    </div>
  );
}
app/routes/sign-in.$.tsx
import { 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:

.env
CLERK_SIGN_IN_URL=/sign-in
CLERK_SIGN_UP_URL=/sign-up
CLERK_SIGN_IN_FALLBACK_URL=/
CLERK_SIGN_UP_FALLBACK_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.

Protecting your pages

Client side

Clerk offers Control Components that allow you to protect your pages. These components are used to control the visibility of your pages based on the user's authentication state.

routes/_index.tsx
import {
  SignOutButton,
  SignedIn,
  SignedOut,
  UserButton,
} from "@clerk/remix";
import { Link } from "@remix-run/react";

export default function Index() {
  return (
    <div>
      <h1>Index Route</h1>
      <SignedIn>
        <p>You are signed in!</p>
        <div>
          <p>View your profile here ๐Ÿ‘‡</p>
          <UserButton />
        </div>
        <div>
          <SignOutButton />
        </div>
      </SignedIn>
      <SignedOut>
        <p>You are signed out</p>
        <div>
          <Link to="/sign-in">Go to Sign in</Link>
        </div>
        <div>
          <Link to="/sign-up">Go to Sign up</Link>
        </div>
      </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.tsx
import { 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 />
    </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.tsx
import { 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.tsx
import { 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>;
}

The getAuth() helper allows you to access the Auth object, including the current userId. You can use this helper to protect your loader function or get data for the initial render of the route.

routes/example.tsx
export const loader: LoaderFunction = async (args) => {
  // Use getAuth to retrieve user data
  const { userId, sessionId, getToken } = await getAuth(args);

  // If there is no userId, then redirect to sign-in route
  if (!userId) {
    return redirect("/sign-in?redirect_url=" + args.request.url);
  }

  // Use the userId to fetch data from your database
  const posts = await mockGetPosts(userId);

  return { posts };
};

The getAuth() helper allows you to access the Auth object. You can use the returns from getAuth() to protect the action function, update the user or perform mutations on data in your database.

routes/example.tsx
export const action: ActionFunction = async (args) => {

  // Use getAuth to retrieve user data
  const { userId, getToken } = await getAuth(args)

  // Use the userId to perform a mutation on data in your database
  const updatedPost = await mockUpdatePost(userId, postId, body)

  return { updatedPost };
}
routes/profile.tsx
import { 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) };
};
routes/profile.tsx
import { ActionFunction, redirect } from "@remix-run/node";
import { getAuth } from "@clerk/remix/ssr.server";
import { createClerkClient } from '@clerk/remix/api.server';

export const action: ActionFunction = async (args) => {
  // Use getAuth to retrieve user data
  const { userId } = await getAuth(args)

  // If there is no userId, then redirect to sign-in route
  if (!userId) {
    return redirect("/sign-in?redirect_url=" + args.request.url);
  }
  // Prepare the data for the mutation
  const params = { firstName: 'John', lastName: 'Wicker' };

  // Initialize clerkClient and perform the mutations
  const updatedUser = await createClerkClient({ secretKey: process.env.CLERK_SECRET_KEY }).users.updateUser(userId, params);

  // Return the updated user
  return { serialisedUser: JSON.stringify(updatedUser) };
};

Next steps

Now that you have an application integrated with Clerk, you will want to read the following documentation:

Customization & Localization

Learn how to customize and localize the Clerk components.

Authentication Components

Learn more about all our authentication components.

Client Side Helpers

Learn more about our client side helpers and how to use them.

Feedback

What did you think of this content?