Next.js LocalStorage : Dark Mode Toggle Example Tutorial

LocalStorage is a web storage object which allows the storage of data persistently in the browser. The data stored in LocalStorage does not get deleted even after the browser is closed. This makes it a powerful option for keeping persistent information and maintaining it across various sessions. Next js has the important features of server-side…

By.

min read

LocalStorage is a web storage object which allows the storage of data persistently in the browser. The data stored in LocalStorage does not get deleted even after the browser is closed. This makes it a powerful option for keeping persistent information and maintaining it across various sessions.

Next js has the important features of server-side rendering and static site generation, LocalStorage comes into action when we need to persist data on the client side.

In this guide, you will learn various aspects of using Local Storage in the Next js application like handling errors, using it in hooks and also how to create a custom hook for local storage.

 

Setting up LocalStorage in Next.js

First, we will set up a Next.js application by executing the following command in the terminal:

npx create-next-app next-local-storage

 

Navigate into the new application directory:

cd next-local-storage

 

Now, update the index.js file which will use the local storage. For this example, we will create a simple application to store a user’s favourite number in LocalStorage.

import { useState } from "react";

export default function Home() {
  let value;

  if (typeof window !== "undefined") {
    value = localStorage.getItem("favoriteNumber") || "";
  }

  const [favoriteNumber, setFavoriteNumber] = useState(value);

  const saveToLocalStorage = e => {
    e.preventDefault();
    localStorage.setItem("favoriteNumber", favoriteNumber);
  };

  return (
    <div>
      <label htmlFor="number">Your favorite number</label>
      <form onSubmit={saveToLocalStorage}>
        <input
          id="number"
          value={favoriteNumber}
          onChange={e => setFavoriteNumber(e.target.value)}
        />
        <input type="submit" value="Save" />
      </form>
    </div>
  );
}

We are checking if the window object is defined before trying to access localStorage. This is because localStorage is part of the window object, which is not available during the server-side rendering.

We also used the useState hook to keep track of the favourite number and update it in localStorage when the form is submitted.

 

Handling Errors with LocalStorage in Next.js

When working with LocalStorage in Next.js, we might face some common errors. The most frequent error is ReferenceError: localStorage is not defined.

This error occurs because Next.js executes code on the server side at first and the local storage object is not available on the server.

Checking if the window is defined

We can add a check to see if window is defined before accessing the localStorage. This will ensure that localStorage is only accessed when a window object is available and code is getting executed on client side.

let value;

if (typeof window !== "undefined") {
  value = localStorage.getItem("favoriteNumber") || "";
}

Using a try-catch block

We can also use a try-catch block to catch the error and handle it accordingly.

let value;

try {
  value = localStorage.getItem("favoriteNumber") || "";
} catch (error) {
  console.log(error);
}

By adding a conditional expression or try-catch block, you can easily handle the LocalStorage undefined issue in the Next js app.

 

Using LocalStorage with Hooks

In React, we frequently use the useState hook, which allows to add React state to function components. Another useful hook is the useEffect hook, which helps to perform side effects in the function components.

While using LocalStorage in Next.js, hooks can be very useful. For instance, you can use the useState and useEffect hooks to sync a part of the state with LocalStorage. For an example of how you can use LocalStorage with hooks:

import { useState, useEffect } from "react";

export default function Home() {
  const [favoriteNumber, setFavoriteNumber] = useState("");

  useEffect(() => {
    const value = localStorage.getItem("favoriteNumber") || "";
    setFavoriteNumber(value);
  }, []);

  const saveToLocalStorage = e => {
    e.preventDefault();
    localStorage.setItem("favoriteNumber", favoriteNumber);
  };

  return (
    <div>
      <label htmlFor="number">Your favorite number</label>
      <form onSubmit={saveToLocalStorage}>
        <input
          id="number"
          value={favoriteNumber}
          onChange={e => setFavoriteNumber(e.target.value)}
        />
        <input type="submit" value="Save" />
      </form>
    </div>
  );
}

Here we are using the useState hook to keep the value of the favourite number, at the same time, we are using the useEffect hook to fetch its value from LocalStorage when the component is mounted. The favourite number value is saved to local storage on form submit.

 

Creating a Custom Hook for LocalStorage

Now we will dive deep into advanced usage of LocalStorage in the Next js application. The Custom hooks are used to reuse the stateful logic by remembering the current value by setting a subscription across components.

Custom hooks provide an easier way to organize code, making it more readable and easy to test. It also helps to avoid wrapper hell in complex applications which mainly occurs when components get wrapped into multiple high-order components.

Let’s have a look at how to create a custom hook for LocalStorage. This custom hook will allow us to get and set values in LocalStorage similar to what we do with the useState hook:

import { useState } from "react";

const useLocalStorage = (key, initialValue) => {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.log(error);
      return initialValue;
    }
  });

  const setValue = value => {
    try {
      const valueToStore =
        value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.log(error);
    }
  };

  return [storedValue, setValue];
};

export default useLocalStorage;

Above, we created a useLocalStorage hook which will take a key and its initial value. It is using the useState hook to create a state variable and a setter function. The new value is stored in LocalStorage when the state is set using the setValue function. This results in sync of state and local storage.

 

Using LocalStorage for Dark Mode Toggle

By using the LocalStorage we will implement a very popular feature of Dark Mode. The dark mode includes dark colours of the theme which reduce the stress on the eye on reading content on screens.

If a user toggles the dark mode for an application, we need to keep the selection saved in the user’s browser so that if that application is opened again, we can fetch that preference and toggle the mode again.

We can easily store the user preference in the Local storage key which will remain there even if the window or browser is closed.

Example of how you can use LocalStorage for a dark mode toggle:

import { useEffect } from "react";
import useLocalStorage from "./useLocalStorage";

export default function App() {
  const [darkMode, setDarkMode] = useLocalStorage("darkMode", false);

  useEffect(() => {
    if (darkMode) {
      document.body.classList.add("dark-mode");
    } else {
      document.body.classList.remove("dark-mode");
    }
  }, [darkMode]);

  return (
    <div>
      <button onClick={() => setDarkMode(!darkMode)}>
        Toggle Dark Mode
      </button>
    </div>
  );
}

By checking the user preference saved in local storage, we are adding/ removing the “dark-mode” class on the document body. Which can be used to style the elements for dark theme by inheriting the child classes very easily.

 

Conclusion

In this guide, we have discussed how to use local storage in the Next js application, How to handle the issues of LocalStorage being undefined by adding a conditional expression or by wrapping it in a try-catch block.

We also discussed how to create a custom hook for the set and get local storage values easily which also prevents the wrapper hell issue on messy high-order components.

We implemented a practical use of LocalStorage example in Next js by adding a Dark Mode toggle switch which saves the user preference in the local storage object of the user’s browser. I hope this will be helpful.

Leave a Reply

Your email address will not be published. Required fields are marked *