Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
N

next-redux-wrapper

> 前端框架
Open source

Redux wrapper for Next.js

2.7K stars0 likes0 views
WebsiteGitHub

About

Redux wrapper for Next.js

Redux Wrapper for Next.js

A HOC that brings Next.js and Redux together

:warning: The current version of this library only works with Next.js 9.3 and newer. If you are required to use Next.js 6-9 you can use version 3-5 of this library, see branches. Otherwise, consider upgrading Next.js. :warning:

Contents:

  • Motivation
  • Installation
  • Usage
    • State reconciliation during hydration
    • Configuration
    • getStaticProps
    • getServerSideProps
    • Page.getInitialProps
    • App
    • App and getServerSideProps or getStaticProps at page level
  • How it works
  • Tips and Tricks
    • Redux Toolkit
    • Server and Client state separation
    • Document
    • Error Pages
    • Async actions
    • Custom serialization and deserialization
    • Usage with Redux Saga
    • Usage with Redux Persist
  • Upgrade from 6.x to 7.x
  • Upgrade from 5.x to 6.x
  • Upgrade from 1.x to 2.x
  • Resources

Motivation

Setting up Redux for static apps is rather simple: a single Redux store has to be created that is provided to all pages.

When Next.js static site generator or server side rendering is involved, however, things start to get complicated as another store instance is needed on the server to render Redux-connected components.

Furthermore, access to the Redux Store may also be needed during a page's getInitialProps.

This is where next-redux-wrapper comes in handy: It automatically creates the store instances for you and makes sure they all have the same state.

Moreover it allows to properly handle complex cases like App.getInitialProps (when using pages/_app) together with getStaticProps or getServerSideProps at individual page level.

Library provides uniform interface no matter in which Next.js lifecycle method you would like to use the Store.

In Next.js example https://github.com/vercel/next.js/blob/canary/examples/with-redux-thunk/store.js#L23 store is being replaced on navigation. Redux will re-render components even with memoized selectors (createSelector from recompose) if store is replaced: https://codesandbox.io/s/redux-store-change-kzs8q, which may affect performance of the app by causing a huge re-render of everything, even what did not change. This library makes sure store remains the same.

Installation

npm install next-redux-wrapper react-redux --save

Note that next-redux-wrapper requires react-redux as peer dependency.

Usage

Live example: https://codesandbox.io/s/next-redux-wrapper-demo-7n2t5.

All examples are written in TypeScript. If you're using plain JavaScript just omit type declarations. These examples use vanilla Redux, if you're using Redux Toolkit, please refer to dedicated example.

Next.js has several data fetching mechanisms, this library can attach to any of them. But first you have to write some common code.

Please note that your reducer must have the HYDRATE action handler. HYDRATE action handler must properly reconciliate the hydrated state on top of the existing state (if any). This behavior was added in version 6 of this library. We'll talk about this special action later.

Create a file named store.ts:

…
Same code in JavaScript (without types)
// store.js

import {createStore} from 'redux';
import {createWrapper, HYDRATE} from 'next-redux-wrapper';

// create your reducer
const reducer = (state = {tick: 'init'}, action) => {
  switch (action.type) {
    case HYDRATE:
      return {...state, ...action.payload};
    case 'TICK':
      return {...state, tick: action.payload};
    default:
      return state;
  }
};

// create a makeStore function
const makeStore = context => createStore(reducer);

// export an assembled wrapper
export const wrapper = createWrapper(makeStore, {debug: true});

wrapper.useWrappedStore

It is highly recommended to use pages/_app to wrap all pages at once, otherwise due to potential race conditions you may get Cannot update component while rendering another component:

import React, {FC} from 'react';
import {Provider} from 'react-redux';
import {AppProps} from 'next/app';
import {wrapper} from '../components/store';

const MyApp: FC<AppProps> = ({Component, ...rest}) => {
  const {store, props} = wrapper.useWrappedStore(rest);
  return (
    <Provider store={store}>
      <Component {...props.pageProps} />
    </Provider>
  );
};

Instead of wrapper.useWrappedStore you can also use legacy HOC, that can work with class-based components.

:warning: Next.js provides generic getInitialProps when using class MyApp extends App which will be picked up by wrapper, so you must not extend App as you'll be opted out of Automatic Static Optimization: https://err.sh/next.js/opt-out-auto-static-optimization. Just export a regular Functional Component as in the example above.

import React from 'react';
import {wrapper} from '../components/store';
import {AppProps} from 'next/app';

class MyApp extends React.Component<AppProps> {
  render() {
    const {Component, pageProps} = this.props;
    return <Component {...pageProps} />;
  }
}

export default wrapper.withRedux(MyApp);

State reconciliation during hydration

Each time when pages that have getStaticProps or getServerSideProps are opened by user the HYDRATE action will be dispatched. This may happen during initial page load and during regular page navigation. The payload of this action will contain the state at the moment of static generation or server side rendering, so your reducer must merge it with existing client state properly.

Simplest way is to use server and client state separation.

Another way is to use https://github.com/benjamine/jsondiffpatch to analyze diff and apply it properly:

…

Or like this (from with-redux-wrapper example in Next.js repo):

const reducer = (state, action) => {
  if (action.type === HYDRATE) {
    const nextState = {
      ...state, // use previous state
      ...action.payload, // apply delta from hydration
    };
    if (state.count) nextState.count = state.count; // preserve count value on client side navigation
    return nextState;
  } else {
    return combinedReducer(state, action);
  }
};

Configuration

The createWrapper function accepts makeStore as its first argument. The makeStore function should return a new Redux Store instance each time it's called. No memoization is needed here, it is automatically done inside the wrapper.

createWrapper also optionally accepts a config object as a second parameter:

  • debug (optional, boolean) : enable debug logging
  • serializeState and deserializeState: custom functions for serializing and deserializing the redux state, see Custom serialization and deserialization.

When makeStore is invoked it is provided with a Next.js context, which could be NextPageContext or AppContext or getStaticProps or getServerSideProps context depending on which lifecycle function you will wrap.

Some of those contexts (getServerSideProps always, and NextPageContext, AppContext sometimes if page is rendered on server) can have request and response related properties:

  • req (IncomingMessage)
  • res (ServerResponse)

Although it is possible to create server or client specific logic in both makeStore, I highly recommend that they do not have different behavior. This may cause errors and checksum mismatches which in turn will ruin the whole purpose of server rendering.

getStaticProps

This section describes how to attach to getStaticProps lifecycle function.

Let's create a page in pages/pageName.tsx:

import React from 'react';
import {NextPage} from 'next';
import {useSelector} from 'react-redux';
import {wrapper, State} from '../store';

export const getStaticProps = wrapper.getStaticProps(store => ({preview}) => {
  console.log('2. Page.getStaticProps uses the store to dispatch things');
  store.dispatch({
    type: 'TICK',
    payload: 'was set in other page ' + preview,
  });
});

// you can also use `connect()` instead of hooks
const Page: NextPage = () => {
  const {tick} = useSelector<State, State>(state => state);
  return 
;
};

export default Page;
Same code in JavaScript (without types)
import React from 'react';
import {useSelector} from 'react-redux';
import {wrapper} from '../store';

export const getStaticProps = wrapper.getStaticProps(store => ({preview}) => {
  console.log('2. Page.getStaticProps uses the store to dispatch things');
  store.dispatch({
    type: 'TICK',
    payload: 'was set in other page ' + preview,
  });
});

// you can also use `connect()` instead of hooks
const Page = () => {
  const {tick} = useSelector(state => state);
  return 
;
};

export default Page;

:warning: Each time when pages that have getStaticProps are opened by user the HYDRATE action will be dispatched. The payload of this action will contain the state at the moment of static generation, it will not have client state, so your reducer must merge it with existing client state properly. More about this in Server and Client State Separation.

Although you can wrap individual pages (and not wrap the pages/_app) it is not recommended, see last paragraph in usage section.

getServerSideProps

This section describes how to attach to getServerSideProps lifecycle function.

Let's create a page in pages/pageName.tsx:

…
Same code in JavaScript (without types)
…

:warning: Each time when pages that have getServerSideProps are opened by user the HYDRATE action will be dispatched. The payload of this action will contain the state at the moment of server side rendering, it will not have client state, so your reducer must merge it with existing client state properly. More about this in Server and Client State Separation.

Although you can wrap individual pages (and not wrap the pages/_app) it is not recommended, see last paragraph in usage section.

Page.getInitialProps

import React, {Component} from 'react';
import {NextPage} from 'next';
import {wrapper, State} from '../store';

// you can also use `connect()` instead of hooks
const Page: NextPage = () => {
  const {tick} = useSelector<State, State>(state => state);
  return 
;
};

Page.getInitialProps = wrapper.g

GitHub Issues· 0 open

View all on GitHub

No open issues yet, or sync has not completed.

Highlights

  • •Motivation
  • •Installation
  • •State reconciliation during hydration
  • •Configuration
  • •getStaticProps
  • •getServerSideProps
  • •Page.getInitialProps
  • •App and getServerSideProps or getStaticProps at page level
  • •How it works
  • •Tips and Tricks

> Tags

TypeScriptisomorphicnextjsreactreact-redux

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category前端框架
PricingOpen source

> Related tools

R
React
用于构建用户界面的 JavaScript 库
V
Vue.js
渐进式 JavaScript 框架
N
Next.js
基于 React 的全栈 Web 框架