Moshe Simantov
About me

How I solve Next.js v13 cookies for server-side rendering (SSR)

How I solve Next.js v13 cookies for server-side rendering (SSR)

Introduction

Next.js v13

Next.js 13, the highly popular React framework, introduces a raft of transformative features aimed at creating more dynamic and efficient web experiences. Leveraging the utility of the new app directory, the system enables easier and faster routing and layouts. The entire process becomes streamlined with layouts allowing shared UI between routes, state preservation, and a reduction in re-renders. Next.js 13 also supports server components for the most dynamic applications and introduces streaming to allow for instant loading states.

A significant development is the introduction of Turbopack, a Rust-based Webpack replacement, promising up to 700x faster updates. It comes with a new and improved next/image component that supports faster native browser lazy loading. The @next/font component makes automatic self-hosting of fonts easier, eliminating layout shifts. Reinventing the next/link API simplifies making automatic <a>. The version also includes a host of incremental improvements to Next.js 12 that collectively promise a better, faster, and more developer-friendly experience.

Client and server-side components

One of the key updates in Next.js 13 is the support for both Client Components and Server Components, which are part of React's new Server Components architecture. This setup allows developers to leverage both the server and client to optimally use what each is great at, facilitating the creation of highly interactive applications with a single programming model.

Server Components are designed to render on the server and send HTML directly to the client. These components enable developers to build complex user interfaces while significantly reducing the amount of JavaScript sent to the client as they do not contain client-side interactivity. This makes it faster for initial page loads and enables a server-first approach with a controlled runtime size that remains constant, irrespective of your application's growth.

On the other hand, Client Components run either on the client-side or the server-side, and can use the full range of React's capabilities, including state, effects, and access to the DOM. Client components are hydration boundary, which means other components outside this boundary won't need to be hydrated.

With this flexibility, developers can decide which components of their app need the power and capability of the client, and which can be pre-rendered on the server side, enhancing performance and user experience. This also improves performance due to efficient code-splitting, as only required components are loaded for a given route, reducing the amount of JavaScript that needs to be parsed and executed.

The problem with cookies

While Next.js 13 is a significant improvement over its predecessor, it does have its share of issues. One of the most significant issues is the lack of cookies during server-side rendering (SSR). This is a problem that has been around for a while, and with no official solution in sight, developers have had to come up with their own workarounds.

Next.js does not have access to cookies during the server-side rendering (SSR), while the client does. This means that if you set a cookie on the client-side, it will not be available during server-side rendering and can break your hydrated application.

It's worth mention that server-side components also can't change cookies, as it will change the application state and require sending headers to the client before the component is rendered. So we have a weird situation that reading cookies using the cookies() helper is possible, but changing them is not.

Searching for a solution

Context provider

In orderr to solve this problem I had no choise but to create some sort of a wrapper to Next.js server cookies() and allow to use it while rendering client-side components on the server. The best way to do it is to create a custom context provider that will read the cookies from the server and pass them to the client-side components:

import { cookies } from 'next/headers';
import { ClientCookiesProvider } from './provider';

export default function RootLayout({ children }) {
  return (
    <ClientCookiesProvider value={cookies().getAll()}>
      {children}
    </ClientCookiesProvider>
  );
}

THe provider itself is a simple React context provider that passes the cookies to the useCookies() hook:

'use client';

import { createContext, useContext } from 'react';

const CookiesContext = createContext(null);

export const ClientCookiesProvider = ({ value, children }) => {
  return (
    <CookiesContext.Provider value={value}>{children}</CookiesContext.Provider>
  );
};

useCookies() hook

Great! Now let's make sure our useCookies() hook can create and update cookies as well as read them.

We'll use the js-cookie package for the client side:

export const useCookies = (): Cookies => {
  if (typeof window !== 'undefined') {
    return jsCookies;
  }

  // ...
};

For the server-side rendering we will need to use a little hack from next/navigation called ServerInsertedHTMLContext:

import { ServerInsertedHTMLContext } from 'next/navigation';

// usage example

const insertedHTML = useContext(ServerInsertedHTMLContext);

insertedHTML?.(() => (
  <script
    dangerouslySetInnerHTML={{
      __html: `alert("Hello world!");`,
    }}
  />
));

This will allow us to inject JavaScript code to the client and execute the changes we need when the client is rendered. In addition, I've created a full useCookies() implemtation that will match the js-cookie API (you can see the full source code here).

Conclusion

The next-client-cookies package is a simple solution to a complex problem. It allows you to use cookies in your Next.js application without having to worry about SSR issues. It's easy to use and works with Next.js v13+, so you can start using it today!

👉 Check out the package on GitHub and let me know what you think.

If you have any questions or feedback, please leave them in the comments below. I'd love to hear from you!

Enjoyed the read?

Don't miss out on my next article! to stay updated on the latest insights.

avataravataravataravataravatar

Let's stay in touch!

Join 50+ other developers who receive my thoughts, ideas, and favorite links.

Need advice on your startup?

I'm available for interesting web projects. Send me some details and let's start working!

Hire me