ReactHustle

NextJS Redirect Without Flashing Content

Jasser Mark Arioste

Jasser Mark Arioste

NextJS Redirect Without Flashing Content

Hello hustlers. In this tutorial, you'll learn how to use a server-side redirect to prevent the flashing of content for unauthenticated users. We'll use NextJS Middleware along with the next-auth package for authentication.

Prerequisites #

This guide assumes the following:

  1. You're using NextJS v12.3.4 and above. This gives us the best middleware functionality.
  2. You're using next-auth v4.17 and above. 

Problem with Static Pages #

If you're using next-auth and you have static pages in your app, you can only get the session from the client-side. Sometimes, this creates a delay in getting the user and shows flashing content if not implemented properly:

import { NextPage } from "next";
import { signOut, useSession } from "next-auth/react";
import React from "react";
const HomePage: NextPage = () => {
  const { data } = useSession();
  if (!data?.user) {
    return <div>Unauthorized - 403. Please login</div>;
  }
  return (
    <div>
      <h1>Home Page</h1>
      <p>You are logged in as {data?.user?.email} </p>
    </div>
  );
};
export default HomePage;
12345678910111213141516

Another problem with this approach is that we have to add this logic to every page that needs authentication.

This flashing doesn't occur in server-side generated pages since we can check the session inside getServerSideProps and redirect to the login page as necessary:

Inside a page component:

// pages/admin.tsx
import { GetServerSideProps } from "next";
import { getSession, useSession } from "next-auth/react";
import { GetServerSideProps } from "next";
import { getSession, useSession } from "next-auth/react";
import React from "react";
const AdminPage = () => {
  const { data } = useSession();
  if (!data?.user) {
    return <div>Unauthorized - 403. Please login</div>;
  }
  return <div>AdminPage</div>;
};
export default AdminPage;

export const getServerSideProps: GetServerSideProps = async (ctx) => {
  const session = await getSession({ req: ctx.req });
  if (!session) {
    return {
      redirect: {
        destination: "/login",
        permanent: false,
      },
    };
  }
  return {
    props: {
      session,
    },
  };
};
12345678910111213141516171819202122232425262728293031

Inside _app.tsx:

import "../styles/globals.css";
import type { AppProps } from "next/app";
import { SessionProvider } from "next-auth/react";

function MyApp({ Component, pageProps }: AppProps) {
  const { session, ...restPageProps } = pageProps;
  return (
    <SessionProvider session={session}>
      <Component {...restPageProps} />
    </SessionProvider>
  );
}

export default MyApp;
1234567891011121314

This prevents flashing the unauthorized content since the session is provided via props and the client does not have to make a request using /api/auth/session. The problem with this approach is that, you'll have to add this logic for every protected page in your application.

How can we improve on this?

The Solution #

Since NextJS v12+, we have the ability to process the incoming request before returning the generated page (static or server-side generated). 

Create a middleware.ts file at the root directory of your project and add this logic here:

// middldware.ts
import { getToken } from "next-auth/jwt";
import { NextRequest, NextResponse } from "next/server";
export async function middleware(req: NextRequest) {
  const pathname = req.nextUrl.pathname;
  const protectedPaths = ["/", "/admin"];
  const isPathProtected = protectedPaths?.some((path) => pathname == path);
  const res = NextResponse.next();
  if (isPathProtected) {
    const token = await getToken({ req });
    if (!token) {
      const url = new URL(`/login`, req.url);
      url.searchParams.set("callbackUrl", pathname);
      return NextResponse.redirect(url);
    }
  }

  return res;
}
12345678910111213141516171819

Demo and Code #

The demo is available at Vercel and the full code is available on GitHub.

Conclusion #

We learned how to redirect without flashing content by using NextJS middleware. Middleware is a very powerful feature in NextJS and there's a lot we can do to process the incoming request.

If you like this tutorial, please leave a like or share this article. For future tutorials like this, please subscribe to our newsletter or follow me on Twitter.

What's Next? #

  1. Add role-based authorization for your NextJS app. Differentiate normal users from admin users and what pages they can access.
  2. Implement middleware chaining/stacking. Organize your middleware functions like a pro.
Share this post!

Related Posts