Client-side nav guard antipattern in Next.js app router

Client-side nav guard antipattern in Next.js app router

There are times in webdev when you need to place a nav guard for a route, for signed in pages, or for a specific condition. The ideal way to do this is by using the Next.js app router middleware, since it happens entirely on server-side and prevents a lot of additional wait for resources. However, there is another way to redirect the user on the client-side by creating a component called Redirect.tsx. Although controversial, this can be a useful coding pattern in some cases.

You can create a simple Redirect.tsx helper component in your Next.js application. It is used to redirect the user to another page on the client-side. This component can be useful in situations where you need to redirect the user based on some client-side logic, such as a user action or a specific condition.

Here is the implementation of the Redirect.tsx component:

'use client';

import { useEffect } from 'react';
import { useRouter } from 'next/router';

interface RedirectProps {
  url: string;

const Redirect: React.FC<RedirectProps> = ({ url }) => {
  const router = useRouter();

  useEffect(() => {
  }, []);

  return null;

export default Redirect;

How this works is, when the page is rendered and displayed on the client-side, useEffect is triggered and this redirect happens. (Might give you a clue as to why this isn't optimal)

Once you have created the Redirect.tsx component, you can use it to redirect the user on the client-side by rendering it in your page component. Here is an example usage:

import Redirect from '../components/Redirect';

const MyPage: React.FC = () => {
  // ...
  if (shouldRedirect) {
    return <Redirect url="/new-page" />;
  // ...

In this example, the Redirect component is used to redirect the user to the /new-page URL if shouldRedirect is true.

Further improvements may be added, like a prop for redirect: boolean to do router.replace instead of route.push, avoiding too many changes in browser history.

Why is it an antipattern?

There are multiple issues with this pattern.

Firstly, speed. The redirect happens client-side which means that the entire load will be triggered before this redirect can take place. The middleware will be called twice, first for this redirecting page-load, and then for the page you redirected to. There's multiple similar points of inefficiency. Not to mention that a series of redirects can happen pretty frequently in a complex codebase.

Secondly, complexity. I'm not sure about you, but in a large project, my preference is to keep all routing and navigation logic centralized. This ensures that you don't get stuck untangling the proverbial navigation spaghetti hell, which some have deemed to be less preferred than taking a bullet to the head.

Thirdly, security. This nav-guard is client-side, so it can be fairly easily bypassed. Server-side doesn't have that issue mostly.

Then why bother?

There are some cases where this pattern comes in handy. Some example use cases -

  1. The page load-time is unimportant and the necessary information to infer a redirect is already going to be loaded at client-side
    Making a nav guard client-side in this scenario makes for more modular code, more easily maintainable depending on your app structure. This is typically rare one-off redirects.

  2. The nav-guard is based on client-side info.
    If you need localStorage to determine if the nav-guard needs to be applied, for example. This will generally not be the case for sensitive info, since a user can easily bypass the nav-gaurd. More of for ensuring user flow in the desired path - maybe you want them to be onboarded on to the app if they're opening the site for the first time, and you chose to store that info as localstorage for some weird reason. I don't judge, you do you.
    Similarly, you could have user settings stored on client-side, say the preference for mobile vs desktop site.

There are other useful applications - maybe, in a completely defeatist move, you just wanna punish users whose adblocker blocked your sentry (sigh, we've all been there). Overall, pretty nifty sometimes. General rule-of-thumb is that if you have to use this more than twice or thrice, you might wanna reconsider.