Adebola's Blog

How to use cookies for persisting state/users in NextJS

October 29, 2020

How to use cookies for persisting users in NextJS!

There are a number of ways to persist users in a React or Single Page Appplication. A lot of times, devs generally use localStorage to store user data and load the data from there when required. While this approach works, it’s not the most effective way as it leaves users vulnerable to attacks. Using cookies is a little safer although it’s still not the safest option. Personally, I prefer a mixture of using cookies and JWT’sJSON Web tokens with expiry to persist user session and to force a user to re-login when their session expires. Using JWT’s is out of the scope of this article.

As LocalStorage is undefined on the server-side(since localStorage does not exist on the server), it’s impossible to access localStorage before rendering a route. As such, our best bet is to check if a user’s cookie is valid on the server side before rendering a route.

Getting started using cookies in React/NextJS

To use cookies in NextJS, we need to install 2 packages. For this tutorial, we’ll be using cookie and react-cookie. React-cookie allows us set the cookie from the client side while the cookie package lets us access the set cookie from the server-side. Install both packages by running

npm install react-cookie cookie

Cookie-cutter is a tiny package that does the same thing as react-cookie.

Setting a cookie

With both packages installed, It’s time to set a cookie. Usually, we set a cookie for a user once they’ve succesfully signed in or signed up to our application. To set a cookie on Sign in, follow the example below.

// pages/login.js
import { useCookies } from "react-cookie"

const Login = () => {
  const [setCookie] = useCookies(["user"])

  const handleSignIn = async () => {
    try {
      const response = await yourLoginFunction(username) //handle API call to sign in here.
      const data =

      setCookie("user", JSON.stringify(data), {
        path: "/",
        maxAge: 3600, // Expires after 1hr
        sameSite: true,
    } catch (err) {

  return (
      <label htmlFor="username">
        <input type="text" placeholder="enter username" />

export default Login

In the snippet above, we call the setCookie hook from react-cookies and set it to a default name. In our case, that’s user. We then make a request to sign in a user by calling a function to log the user in. We take the response from that API call, stringify the data(cookies are formatted as text) and store that data in a cookie.

We also pass some additional options to the cookie including path - makes sure your cookie is accessible in all routes, maxAge, how long from the time the cookie is set till it expires and sameSite. Samesite indicates that this cookie can only be used on the site it originated from - It is important to set this to true to avoid errors within firefox logs.

Giving your app access to the Cookie

To ensure that every route in our application has access to the cookie, we need to wrap our APP component in a cookie provider.

Inside _app.js, add the following bit of code.

// pages/_app.js
import { CookiesProvider } from "react-cookie"

export default function MyApp({ pageProps }) {
  return (
      <Component {...pageProps} />

Setting up the function to parse the cookie

Next, we need to setup a function that will check if the cookie exists on the server, parse the cookie and return it. Created a new folder called helpers and within that add an index.js file.

Inside this file, add the following piece of code.

// helpers/index.js

import cookie from "cookie"

export function parseCookies(req) {
  return cookie.parse(req ? req.headers.cookie || "" : document.cookie)

The function above accepts a request object and checks the request headers to find the cookie stored.

Accessing the cookie within your component

Finally, we will use getInitialProps in our component to check if the user already has a valid cookie on the server side before rendering the requested route. An alternative to this approach is using getServerSideProps.

import { parseCookies } from "../helpers/"

export default function HomePage({ data }) {
  return (
        <h1>Homepage </h1>
        <p>Data from cookie: {data.user}</p>

HomePage.getInitialProps = async ({ req, res }) => {
  const data = parseCookies(req)

  // check if there's a valid cookie on the server side. If there isn't, redirect user to index
  if (res) {
    if (Object.keys(data).length === 0 && data.constructor === Object) {
      res.writeHead(301, { Location: "/" })

  return {
    data: data && data,

Within getInitialProps, we’re passing in the request object(req) that’s available to us on the server-side in NextJS to the parseCookies function. This function returns the cookie to us which we can then send back to the client as props.

We also do a check on the server to see if the response object is available. The res object is only available on the server. If a user hits the HomePage route using next/link or next/router, the res object will not be available.

Using the res object, we check if there are cookies and if they’re still valid. We do this check using the res object. If the data object is empty, it means the cookie isn’t valid. If the cookie isn’t valid, we then redirect the user back to the index page rather than showing a flash of the HomePage before redirecting the user.

Note that subsequent requests to pages containing getInitialProps using next/link or next/router will be done from the client side. i.e The cookie will be extracted from the clent rather than the server side for other routes that are accessed via using next/link or next/router

And with that, you can now store cookies for users in your application, expire those cookies and secure your app to a good extent.

Written by Adebola Adeniran, I love Nigerian Jollof (> Ghana jollof) and Plantain. Follow me on Twitter.