Using Redux with NextJs 13 App Router
With Next.js 13 introducing server components as the default, developers must adapt their Redux implementation. This article explores how to modify Redux usage by utilizing the 'use client' directive, creating a custom ReduxProvider, and strategically placing client components. Adhering to these best practices ensures that the benefits of server rendering are maximized while maintaining client-side functionality and state management with Redux.
With the new NextJs 13 app router, NextJs has made all the components by default server components. This changes a lot of things and introduces different ways of doing things in the new app router.
One of the things that need some slight modifications is the way we used (defined and used) Redux.
Creating and Providing the Redux
There are some differences in using Redux with NextJs 13 new app router as compared to the old router (before version 13). These differences are due to the fact that:
All components are âServer Componentsâ by default
Since all components are server components by default, we cannot use state related or other hooks in server components. To use such features, we need to make the component a client component, which is done by putting âuse clientâ at the top of file.
There is not _app.js where we can add <Provider>
to the top.
We need to put the <Provider>
(from react-redux) at the top of hierarchy so it's accessible to all components down below, we have layout.js file for this in the new app router.
Bad approach
One way we can do this is simply put the use client at the top of root layout.js file and provide our store there but this will make all the child components client components. This is not ideal because by doing this we will not be able to use server component benefits.
Good approach
- Create a custom provider component with use client string.
- Wrap it around the layout.js children.
- Provide the store in provider component.
- Return the <span>
{children}
</span> from the provider component that are passed to it.
layout.js
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>
<ReduxProvider>{children}</ReduxProvider>
</body>
</html>
);
}
ReduxProvider.js
"use client";
import { Provider } from "react-redux"
import reduxStore from "./store";
export default function ReduxProvider({ children }) {
return <Provider store={reduxStore}>{children}</Provider>;
};
Using Redux (Patterns)
As we learned before, Redux lives on the client side, so we would need to make the components client components to use redux in them. There are a few patterns provided by NextJs to optimize our application when using server and client components together.
Moving Client components to the leaf
What we can do to optimize our web app is to move client components i.e., components that need interactivity or components that use redux features in our case) as much down the tree/ subtree as possible. This ensures that maximum number of components are server rendered.
Passing server components as props
Situation might arise where we would need to import a server component inside a client component, but we cannot import server components inside a client component â it's an antipattern! -- because by doing this Next needs to make another round trip for rendering the nested (server component inside client component).
The need for importing can occur in cases where a component that is interactive needs to show some data that is fetched in a server component. We could import the server component directly in the client component but the supported pattern is to pass the server component as a prop to the client component from the parent component.
Following these patterns can ensure that our app is optimized, and we can offer a better user experience.
This article was written by Usama Hafeez, a full-stack engineer @ antematter.io.