NextJS 14 Auth with Redux and Superbase

In the ever-evolving landscape of web development, harnessing the power of modern frameworks and tools is key to building efficient, scalable, and dynamic applications. Next.js 14 emerges as a frontrunner, offering unparalleled support for React-based server-side rendering and static site generation, while Redux provides a robust state management solution, ensuring a predictable state across your application. Adding Supabase into the mix, with its easy-to-use and scalable backend services, developers can now leverage the best of both worlds: a powerful frontend with Next.js and Redux, and a flexible, PostgreSQL-powered backend. This blog post aims to demystify the process of integrating these cutting-edge technologies. Whether you're building a full-fledged e-commerce site, a SaaS application, or anything in between, follow along as we explore how to seamlessly use Next.js 14 with Redux and Supabase, unlocking a new horizon of web development possibilities.

Clone, Download or Fork

This is a tutorial on how to use NextJS 14 with Redux and Superbase. This tutorial is a work in progress and will be updated as I continue to work on it. If you would like to see the code, you can find it on my GitHub

Create a new NextJS project

First, we need to create a new NextJS project. We can do this by running the following command in our terminal:

npx create-next-app@latest

Add neccessary dependencies

Next, we need to install the necessary dependencies for our project. We will need the following packages:

project-folder/package.json


  {
    "name": "project-name",
    "version": "0.1.0",
    "private": true,
    "scripts": {
      "dev": "next dev",
      "build": "next build",
      "start": "next start",
      "lint": "next lint"
    },
    "dependencies": {
      "@reduxjs/toolkit": "^1.9.7",
      "@supabase/auth-ui-react": "^0.4.6",
      "@supabase/auth-ui-shared": "^0.1.8",
      "@supabase/ssr": "^0.0.4",
      "@supabase/supabase-js": "^2.38.4",
      "@vercel/analytics": "^1.1.1",
      "next": "14.1.0",
      "prismjs": "^1.29.0",
      "react": "^18",
      "react-dom": "^18",
      "react-redux": "^8.1.3"
    },
    "devDependencies": {
      "@types/node": "20.11.17",
      "@types/react": "18.2.55",
      "eslint": "^8",
      "eslint-config-next": "14.1.0",
      "sass": "^1.70.0"
    }
  }

Create Supabase Route

Create a new folder called supabase in the root of your project called supabase and create a file called supabaseRoute.js and add the following code:

project-folder/supabase/supabaseRoute.js


  import { createServerClient } from "@supabase/ssr";
  import { cookies } from "next/headers";
  
  export function getSupabaseRoute(request) {
    const cookieStore = cookies();
  
    return createServerClient(
      process.env.NEXT_PUBLIC_SUPABASE_URL,
      process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
      {
        cookies: {
          get(name) {
            return cookieStore.get(name)?.value;
          },
          set(name, value, options) {
            cookieStore.set({ name, value, ...options });
          },
          remove(name, options) {
            cookieStore.set({ name, value: "", ...options });
          },
        },
      }
    );
  }
  

Create Middleware

Create the middleware.ts file in the root of your project and add the following code:

project-folder/middleware.ts


  import { createServerClient, type CookieOptions } from "@supabase/ssr";
  import { NextResponse, type NextRequest } from "next/server";
  
  export async function middleware(request: NextRequest) {
    let response = NextResponse.next({
      request: {
        headers: request.headers,
      },
    });
  
    const supabase = createServerClient(
      process.env.NEXT_PUBLIC_SUPABASE_URL!,
      process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
      {
        cookies: {
          get(name: string) {
            return request.cookies.get(name)?.value;
          },
          set(name: string, value: string, options: CookieOptions) {
            request.cookies.set({
              name,
              value,
              ...options,
            });
            response = NextResponse.next({
              request: {
                headers: request.headers,
              },
            });
            response.cookies.set({
              name,
              value,
              ...options,
            });
          },
          remove(name: string, options: CookieOptions) {
            request.cookies.set({
              name,
              value: "",
              ...options,
            });
            response = NextResponse.next({
              request: {
                headers: request.headers,
              },
            });
            response.cookies.set({
              name,
              value: "",
              ...options,
            });
          },
        },
      }
    );
  
    await supabase.auth.getSession();
  
    return response;
  }
  
  

Add neccessary dependencies

Next, we need to install the necessary dependencies for our project. We will need the following packages:

project-folder/src/app/_components/redux-provider/ReduxProvider.js


  "use client";
  import { Provider } from "react-redux";
  import { store } from "../../_redux/store";
  
  const ReduxProvider = ({ children }) => {
    return <Provider store={store}>{children}</Provider>;
  };
  
  export default ReduxProvider;
  

Create Redux Store

Next, we need to create a Redux store to manage our application state. We will need to create a store file and import the necessary reducers.

project-folder/src/app/_redux/store.js


  import { configureStore } from "@reduxjs/toolkit";
  import sessionReducer from "./features/session/sessionSlice";
  import siteReducer from "./features/site/siteSlice";
  export const store = configureStore({
    reducer: {
      session: sessionReducer,
      site: siteReducer,
    },
  });

Create Redux Session Slice

Next, we will create a new file in the _redux/features/session folder called sessionSlice.js. This file will contain the Redux slice that will manage our session state.

project-folder/src/app/_redux/features/session/sessionSlice.js


  import { createSlice } from "@reduxjs/toolkit";

  const initialState = {
    session: null,
  };
  
  export const sessionSlice = createSlice({
    name: "session",
    initialState,
    reducers: {
      setSession: (state, action) => {
        state.session = action.payload;
      },
      clearSession: (state) => {
        state.session = null;
      },
    },
  });
  
  // Action creators are generated for each case reducer function
  export const { setSession, clearSession } = sessionSlice.actions;
  
  export default sessionSlice.reducer;