A next-redux-wrapper extension to sync a subset of a client's Redux state with cookies such that it survives page reloads and is available to the server during SSR 🍪 ✨
When it comes to Redux state persistence, Redux Persist is a popular choice. With Next.js, however, the persisted state is (without further ado) not available during SSR. Hence, the first render on the client side may largely differ from the server-rendered markup. A solution to this is a storage method that is available to both the server and the client by default: Cookies.
This library started as a drop-in replacement for next-redux-wrapper that built upon Redux Persist and a storage adapter for cookies.
However, in response to getStaticProps()
and getServerSideProps()
being introduced in Next.js, it has been rewritten and the tooling has been simplified significantly.
What remains is a single Redux middleware and a tiny wrapper around the makeStore()
function.
On the server, a wrapper around makeStore()
passes the Next.js context to the middleware via an action.
The middleware then reads the cookies and dispatches an initial HYDRATE
action with the client's state.
On server-side state changes, set-cookie
headers are set to update the client's cookies.
Similarly, the client updates cookies whenever a relevant portion of the state changes.
Moreover, the HYDRATE
action is intercepted on the client and the configured state subtrees are (by default) parsed from the cookies instead of the retrieved JSON data.
This way, incoming state updates from getStaticProps()
do not overwrite the synced state subtrees as getStaticProps()
does not update the cookies.
You can opt out of this behavior on a per-state-subtree basis and instead always receive the server's state in the HYDRATE
reducer if you wish to handle state portions from getStaticProps()
on your own.
Some words about compression:
By default, the serialized cookie state is compressed using lz-string to keep the cookie size small.
You can disable compression globally or per state subtree by setting the compress
option to false
.
TL;DR
For a quick working example, check out the demo project in this repository. It uses Redux Toolkit but that should not discourage you.
- Clone the repository
- Make sure you have have NPM v7 or later installed (
npm i -g "npm@>=7"
; required for the workspaces feature)- Run
npm install
in the root directorycd demo && npm start
- Inspect the setup in
store.ts
If you do not have next-redux-wrapper set up, follow their installation instructions.
Afterwards, install next-redux-cookie-wrapper
:
npm install --save next-redux-cookie-wrapper
and configure your store to use nextReduxCookieMiddleware
by passing it to createStore()
and wrapping your makeStore()
function with wrapMakeStore()
:
+ import {nextReduxCookieMiddleware, wrapMakeStore} from "next-redux-cookie-wrapper";
...
- const makeStore = () => createStore(reducer);
+ const makeStore = wrapMakeStore(() =>
+ createStore(
+ reducer,
+ applyMiddleware(
+ nextReduxCookieMiddleware({
+ subtrees: ["my.subtree"],
+ })
+ )
+ )
+ );
That's it! The state of my.subtree
should now be synced with a cookie called my.subtree
and available on the server during SSR.
If not, you can set the debug flag of next-redux-wrapper
to true
to get some insights on the state:
export const wrapper = createWrapper<AppStore>(makeStore, {debug: true});
When using Redux Toolkit, it is important that nextReduxCookieMiddleware
is used before any of the default middlewares:
const makeStore = wrapMakeStore(() =>
configureStore({
reducer: {...},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().prepend(
nextReduxCookieMiddleware({
subtrees: ["my.subtree"],
})
),
})
);
The reason for this is that Redux Toolkit by default adds a serializability middleware that would complain about the SERVE_COOKIES
action which wrapMakeStore()
uses to pass the Next.js context to nextReduxCookieMiddleware
.
When nextReduxCookieMiddleware
is invoked before the serializability middleware, it catches the SERVE_COOKIES
action before it reaches that middleware.
Alternatively, you can also configure the serializability middleware to ignore the SERVE_COOKIES
action, should you prefer that.
For the configuration options of nextReduxCookieMiddleware
, please refer to the API documentation.