Should You Use Redux in Next.js?

Redux is a great choice for state management in React applications. But it has received its fair share of criticism as well.

Any question around Should I use X with Y is very difficult to answer because, in most cases, the answer is:

It depends…

But today, I will try to answer the question of whether we should use Redux in Next.js. And show you some alternative ways of doing things you might want to consider.

My Direct Answer

If you want my direct opinion on this topic, the answer is:

You should really really try to avoid using Redux in Next.js.

Let me explain why.

Redux was invented in the first place to share a common state between complex component hierarchies.

Reason 1: Next.js architecture is not suitable for Redux.

The general architecture of a Next.js application is usually very different from a React application.

In NextJS we have the concepts of getServerSideProps and getStaticProps which can populate the data required for a page before rendering.

So using Redux in Next.js often doesn’t make that much sense.

Reason 2: Redux has other alternatives.

React now supports Context, which can share the common state between components.

If you have a lot of nested components that need a shared piece of data, then you can just use React Context. Like the following:

const Context = createContext(MockData);
..
const contextData = useContext(Context);

There are many scenarios where you wouldn’t even need any centralized state management solution. I will show that today as well!

Reason 3: It’s very, very complex to set up.

After knowing all of these, if you still feel that you will need Redux in your Next.js application, just a heads up

It’s really complex to set up Redux with Next.js

You will need a special package named next-redux-wrapper to get it to work. Even after that, handling the server and client states and making them sync is a lot of pain. Read more about that here:

https://github.com/kirill-konshin/next-redux-wrapper

Reason 4: Optimizing Redux in Next.js is complex.

Even after all of these, if you manage to integrate Redux with Next.js, the next problem you will face is performance.

One big argument against React Context is its performance implications. And using Redux can sometimes solve that issue with the use of selectors.

const selectProducts = state => state.products

Well, it’s hard to do in Next.js.

I am not saying it’s impossible, but optimizing Redux properly for performance in Next.js can be very hard and require a very long time.

Okay, but what are my options?

Glad you asked; I am not saying that you should not use Redux in Next.js applications.

All I am saying is,

Ask yourself if you really need Redux.

I will present some scenarios where you might think Redux is the way to go, but there are better alternatives in reality.

Let’s explore, shall we?

Scenario 1: You already know what’s on the page.

Let’s just say you are building an e-commerce application where most of your URLs are pre-determined.

In a normal React application, we might think of fetching the details of the product and saving it to a Redux state and show on the page, but in Next.js, there is a better way to do this: ``` /products -> Shows the list of the product

/product/{productId} -> Shows the details of the product ``` So if you know that users will go to a page, /product/{productId} then you already know the productId even before you load the page.

You can just pre-fetch the data, pre-generate the page, and send it to your visitors, improving your application's performance.

In these cases, you can use either getStaticPropsbecause you already know what data will be there on the page already. It helps with the caching too!

export async function getStaticProps(context: GetStaticPropsContext) {
    
    const products = await getProducts();
    
    return {
        props: {
            products
        }
    };
}

If the product list change over time, you can use getServerSideProps instead of this function.

export async function getServerSideProps(context: GetServerSidePropsContext) {
    const request = context.req;
   
    const products: getProductList()
  
    return { 
      props: {
        products
      }
    }
}

So if you know what data is going to be rendered on the page before you load the actual page, then you can just use getStaticProps and getServerSideProps and get the job done like a boss.

Scenario 2: You don’t know what’s coming

This is a very common scenario in most real-life applications. We might know what comes in the initial page load, but the page's content depends on the user’s action.

A good example can be a product page with pagination. So you only know what to load on the first page. But after the initial page is loaded, the users might want to see the next page.

Like the following pagination on Amazon’s website:

Amazon’s Pagination Component

In this case, it’s tricky to use static generation because you have to fetch fresh data. Should you use redux in this case?

The answer is No. You would be better off using some kind of query libraries like swr or react-query to manage the API data.

Following is an example using react-query:

const [page, setPage] = React.useState(0)

const fetchProducts = (page = 0) => fetch('/api/products?page=' + page).then((res) => res.json())

const { data} = useQuery(['products', page], () => fetchProducts(page), { keepPreviousData : true })

So why would you fetch data and store it in the redux store when you can get awesome features like caching and pre-fetching without using Redux?

Scenario 3: You need to share some common states between components.

Let’s say your application has some kind of authentication setup. In this case, you will want to share the authentication state between components. Maybe to show and hide the login button.

What do you do now?

You might think, okay, so now I need to use Redux to share the authentication state everywhere.

Umm, actually, no.

If you are handling such simple scenarios, you can just leverage good old browsers LocalStorage. Maybe wrap it up inside a nice little hook:

const useAuthState = () => {
  
  const setAuthState = ( currentState ) => {
    localStorage.set("auth_state" , currentState)
  }
  
  const getAuthState = () => {
    return localStorage.get("auth_state" ,false);
  }
  
  return { setAuthState , getAuthState }
}

If Localstorage is not an option for you, then you should try to set it up React Context first.

But don’t get me wrong.

Redux will work perfectly here. But you should consider using React Context API first, which comes with react and saves you precious bundle size!

And it’s also easier to set up!

Do you still have some other use cases?

After all these, you still have a specific use-case where you need a common state shared between your components, then use Redux.

Just don’t rush to it. Try to understand why you need this first and then use it properly so that it doesn’t create more problems than solving the ones you already have!

Final thoughts

I hope you learned something new today. Have a great day! :D


Share this post


Read more articles...

team

3D Graphics in ReactJS and ThreeJS

team

Learn WebSockets with NodeJS and ReactJS

team

Static Site Generation in NextJS

Profile Image

Who I am

Hi, I amMohammad Faisal, A full-stack software engineer @Cruise , working remotely from a small but beautiful country named Bangladesh.

I am most experienced inReactJS,NodeJS andAWS

Buy Me a Coffee Widget