How To Keep Component State Across Pages in Next.js

Using the session storage & useLayoutEffect

Next makes dividing our app into server-side-rendered pages easy. Using the same component on different pages isn't a problem too — but keeping the state is. By default, when opening a different page, the state of the component is reset. Let's look at a small example.

Given this small button component:

We include it in two different pages, accessible to each other via next/link:

The about.js is basically the same file, so I'll leave you with one snippet.

When putting things together, we notice the following: Incrementing the button works fine. But when switching to the other page, the state is lost. In some apps, this is not what we wish — what we want instead is keeping the state somehow. Here is how to do it easily.

Introducing the session storage

The Web Storage API should be in the toolbox of every developer. It consists of two features for storing data: First, the more popular localStorage, which saves a key-value pair permanently in the browser. Second, the sessionStorage, which saves a key-value pair for the duration of a session. A session? Let me clarify this.

When you open up, your session starts. When you click on a button and navigate to your session still is on. When you close the window, the session is done.

Keeping the state for the duration of a session is a common use case — for example, when you have a search bar and want to keep its input on the results site the user is navigated to.

Hint: There are libraries optimized for persisting states. I only intend to show you how persisting state often works — using the session storage. In most cases, using a library is a better option.

The code

When wanting to keep state across pages, we need to do two things:

  • Save the current state

  • Receive the current state from the storage to write it into the React state

Both are great uses cases for React hooks, more precisely, the useEffect hook. Saving the current state, whenever it is changed, to the sessionStorage is a case for useEffect listening to a single state. Whenever our counter-state changes, we commit the changes to the session storage:

Fetching the session storage and hydrating the component state with it is a case for the useEffect configuration that works like componentDidMount — once the component is mounted.

Yet, this will lead to a small flicker every time we switch the page. Why? Because the code inside useEffect is getting executed after our component is made visible on the screen.

To avoid this, we can use useLayoutEffect as it serves as the useEffect alternative, when the DOM is mutated. Apart from the name, the syntax is almost the same as with useEffect:

In case the session storage key-value pair does already exist, we copy it's data into the state. In case this entry in the session storage does not exist yet, we create it.

Since it is not all black and white, there is a disadvantage of useLayoutEffect: Compared to the classic useEffect, its alternative delays the browser painting. Yet, in our case, this isn't much of a problem in this case.

Putting it all together:

When we now increase the button and switch the pages, the state will be kept — since using useLayoutEffect it is a smooth transition, without a flicker when hydrating the state.

Thank you for reading!

Continue Learning