Cookies and SSR with React and Next.js
Wed Mar 22 2023
Today I was working on something for Islebuilds
which is supposed to be server-side rendered on first load and then client-side rendered afterwards.
Because of this, we need to do some extra stuff if we want to use cookies without having hydration errors.
I was having some issues with react-cookie
so I decided to write my own useCookie
hook which is documented here.
I started by replacing react-cookie
with cookies-next
instead.
But cookies-next
doesn't have React hooks in it, just methods to read and set cookies on the client,
so we'll write out own.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Pretty straightforward.
However, we start having issues when there are multiple uses of useCookie
acting on the same key.
If the first instance's setStateWrapper
is called, the cookie will be updated and its internal state will change
but other instance's of useCookie
will not be notified.
To solve this, we can store a map of keys and listeners:
|
And then register a listener inside of useCookie
.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
We also need to change setStateWrapper
to notify other listeners of the new value.
|
|
|
|
|
|
|
|
Everything works with client-side rendering now!
Adding support for SSR
Next, we need to provide the correct cookies on the server to make sure SSR and CSR produce the same page. We'll do this using React context.
|
|
Then, inside useCookies
, we'll initialize our internal state differently during SSR.
cookies-next
supports SSR by passing req
and res
into getCookie
, but we'll do it a little differently.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
We also need to wrap our app with a CookiesProvider
.
This project is still using Next.js 12 at the time of writing,
so the code for Next.js 13 or other frameworks might be slightly different.
Basically, the whole app has a getInitialProps
in _app.tsx
which parses the cookies
and passes them to the provider.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
I didn't have a need to support setting cookies during SSR so that probably doesn't work with this code.
useBooleanCookie
helper
Here's another short hook that wraps useCookie
to handle boolean values.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
You could easily write another hook like useNumberCookie
or some other type
to provide validation and types as needed.
Complete code for useCookie
The final code for useCookie
looks something like this:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Thanks for reading!