· TanStack Router · 5 min read

TanStack Router: Authenticated routes & Guards

Protect your routes and redirect users to the login page

Protect your routes and redirect users to the login page

Welcome to the fourth article of a series where we will explore TanStack Router, the new typesafe routing library for React.

In this article, we’ll learn how to protect some routes (pages) of your application with a guard. In this specific example, we’re going to redirect unauthorized users to the login page.

Define a Guard

If you’re following the series you already know there’s always a video version showing the full walkthrough. You can find here Chapter 4 or you can keep reading for a collection of the highlights.

Create a login page

In this example we’re going to protect the /profile page, showing it only to logged in users. Let’s begin by creating a basic login page.

import { createFileRoute, useRouter } from '@tanstack/react-router';
import { isAuthenticated, signIn, signOut } from '../utils/auth';

export const Route = createFileRoute('/login')({
  component: Login,
});

function Login() {
  const router = useRouter();

  return (
    <>
      <h2>Login</h2>
      {isAuthenticated() ? (
        <>
          <p>Hello user!</p>
          <button
            onClick={async () => {
              signOut();
              router.invalidate();
            }}
          >
            Sign out
          </button>
        </>
      ) : (
        <button
          onClick={async () => {
            signIn();
            router.invalidate();
          }}
        >
          Sign in
        </button>
      )}
    </>
  );
}

For the sake of the example, I already defined a draft implementation of the auth functions used in this snippet. If you want to follow along with your app, here you are:

export function isAuthenticated() {
  return localStorage.getItem('isAuthenticated') === 'true';
}

export async function signIn() {
  localStorage.setItem('isAuthenticated', 'true');
}

export async function signOut() {
  localStorage.removeItem('isAuthenticated');
}

Protect a route

Until now it was just preparation, if you already have the logic in place for your app here’s where the interesting part begins.

The createFileRoute function takes an extra parameter called beforeLoad and that’s what we’re gonna use.

export const Route = createFileRoute('/login')({
  beforeLoad: async () => {
    if (!isAuthenticated()) {
      throw redirect({ to: '/login' });
    }
  },
  component: Login,
});

With this simple snippet, you already have a working demo, but we’re not done yet.

This was an oversimplified example but most likely on React you’re getting the authenticated boolean from a hook… and if you try to use a hook inside the beforeLoad function, React will tell you that you cannot.

So… let’s see how to deal with that!

Using data from a hook (TanStack Router Context)

In a more likely example, you might have a hook like this (well, hopefully not storing on localStorage but with data coming from/to an API).

export const useAuth = () => {
  const signIn = () => {
    localStorage.setItem('isAuthenticated', 'true');
  };

  const signOut = () => {
    localStorage.removeItem('isAuthenticated');
  };

  const isLogged = () => localStorage.getItem('isAuthenticated') === 'true';

  return { signIn, signOut, isLogged };
};

export type AuthContext = ReturnType<typeof useAuth>;

We now need to inform TanStack Router about this information, using its context.

Defining the context

The first step to define the context is obviously defining its type. See the last line in the previous snippet? We can use that!

type RouterContext = {
  authentication: AuthContext;
};

You can define it in a dedicated file, or directly in your ___root.tsx as it’s the file where you’re gonna use it.

You probably are already using createRootRoute, you can replace it with createRootRouteWithContext like so:

export const Route = createRootRouteWithContext<RouterContext>()({

Don’t forget to add the extra () at the end.

Updating the RouterProvider

You should now see an error in the file where you defined your RouterProvider, since you need to pass the newly defined context.

const router = createRouter({
  routeTree,
  context: { authentication: undefined! },
});

Now your router knows about a context, but it is empty… let’s fill it!

function App() {
  const authentication = useAuth();
  return <RouterProvider router={router} context={{ authentication }} />;
}

And… that’s it!

Reading the context

You can now update the protected page like so:

export const Route = createFileRoute('/profile')({
  beforeLoad: async ({ context }) => {
    const { isLogged } = context.authentication;
    if (!isLogged()) {
      throw redirect({ to: '/login' });
    }
  },
  component: Profile,
});

The difference is that you can now read { context } where you will find data from your hook(s).

Protecting multiple routes at once

A straightforward way to protect multiple routes in a folder, is by creating a file with the same name, defining just the beforeLoad function.

Let’s create a couple of routes we want to protect:

src/
  routes/
    authenticated/
      dashboard.tsx
      profile.tsx

Now, create a file in src/routes/authenticated.ts with the following content:

import { createFileRoute, redirect } from '@tanstack/react-router';

export const Route = createFileRoute('/_authenticated')({
  beforeLoad: async ({ context }) => {
    const { isLogged } = context.authentication;
    if (!isLogged()) {
      throw redirect({ to: '/login' });
    }
  },
});

And that’s it! All files inside the /authenticated folder will be protected.

Removing /authenticated from the URL

You most likely do not want to see the parent route in the URL, which is reasonable. It’s as easy as adding an underscore _ in the route, renaming it to _authenticated.

You can now navigate to https://localhost:5173/dashboard and if you’re logged in, you can access the page!

## Conclusion

We can now protect a single route or a tree of routes, and group some routes in folders that are not shown in the URL.

If a user navigates to one of the protected routes, we can redirect them to a login page.


Watch the full playlist on YouTube: TanStack Router


You can find the full code on this repository on the 04-authenticated-routes. Leave a ⭐️ if you found the demo code useful!


About the author
Leonardo

Hello! My name is Leonardo and as you might have noticed, I like to talk about Web Development and Open Source!

I use GitHub every day and my favourite editor is Visual Studio Code... this might influence a little bit my conent! :D

If you like what I do, you should have a look at my YouTube Channel!

TanStack Router (5 Parts Series)
You might also like
Back to Blog