31

I make a system jsonwebtoken in React and use Next.js. I find a problem when I run the code in the browser, that is, "localStorage is not defined". How can I fix it?

This is my code in file AuthStudentContext.js:

import React from 'react'
import axios from 'axios'

const axiosReq = axios.create()
const AuthStudentContext = React.createContext()

export class AuthStudentContextProvider extends React.Component {

    constructor() {
        super()
        this.state = {
            students: [],
            student: localStorage.getItem('student') || {},
            token: localStorage.getItem('token') || "",
            isLoggedIn: (localStorage.getItem('student' == null)) ? false : true
        }
    }

    login = (credentials) => {
        return axiosReq.post("http://localhost:4000/api/login", credentials)
            .then(response => {
                const { token } = response.data
                localStorage.setItem("token", token)

                this.setState({
                    token,
                    isLoggedIn: true
                })

                return console.log(response)
            })
    }

And it shows error "localStorage is not defined".

4
  • use window.localStorage
    – avck
    Commented Dec 31, 2019 at 6:28
  • 2
    and then window is not defined Commented Dec 31, 2019 at 6:30
  • can you post the snippet with error. window should be available in global namespace
    – avck
    Commented Dec 31, 2019 at 6:31
  • Next is overriding the window object everywhere except component did mount. : stackoverflow.com/a/55151122/1643143
    – avck
    Commented Dec 31, 2019 at 6:34

8 Answers 8

31

As everyone already mentioned, Next.js runs both on the client and server. On the server, there isn't any localStorage, hence the undefined error.

However, an alternative solution is to check if Next.js is running on the server before accessing the localStorage. I.e.,

const ISSERVER = typeof window === "undefined";

if(!ISSERVER) {
    // Access localStorage
    ...localStorage.get...
}
15

In the constructor, as well as componentWillMount lifecycle hooks, the server is still rendering the component. On the other hand, localStorage exists as part of the browser's window global, and thus you can only use it when the component is rendered. Therefore you can only access localStorage in the componentDidMount lifecycle hook. Instead of calling localStorage in the constructor, you can define an empty state, and update the state in componentDidMount when you can start to call localStorage.

constructor() {
  super()
  this.state = {
    students: [],
    student: undefined
    token: undefined,
    isLoggedIn: undefined
  };
}

componentDidMount() {
  this.login();
  this.setState({
    student: localStorage.getItem('student') || {},
    token: localStorage.getItem('token') || "",
    isLoggedIn: (localStorage.getItem('student' == null)) ? false : true
  });
}
0
12

I never touched Next.js, but I guess its equivalent to Nuxt.js. So it does server-side rendering while you try to access localstorage on the client side.

You will need to use componentDidMount() for this. Here is an example:

componentDidMount(){
   localStorage.setItem('myCat', 'Tom');
   alert("Tom is in the localStorage");
}

Otherwise, you could try with process.browser:

if (process.browser) {
   localStorage.setItem("token", token);
}
1
  • i don't want setItem, but i want getItem Commented Dec 31, 2019 at 6:35
5

In addition to what SILENT said, this works for me:

React.useEffect(() => {
    if (localStorage) {
        const getLocalState = localStorage.getItem("headless");
        console.log("LocalState: ", getLocalState)
    }
}, []);
1
  • it will work only if your localstorage data is not depend with your initial browser data. if your browser data is depend with localstorage then it will give issue cause next js render on server side and local storage work for client side
    – lodey
    Commented Mar 23, 2022 at 18:09
3

The window object and Localstorage won't be available when Next.js is building. So you need to check if the code is running in the browser. If you are running in React hooks you don't need to do this because hooks are always running browser side in React.

Just add these two utility functions to your Next.js project.

export const isBrowser = (): boolean => {
  return typeof window !== 'undefined'
}

export const nextLocalStorage = (): Storage | void => {
  if (isBrowser()) {
    return window.localStorage
  }
}

Then you can use it in your code like this:

nextLocalStorage()?.setItem('user', JSON.stringify(user))
1

this is working

export const Theme = { Light: "light", Dark: "dark" };

const [currentTheme, setCurrentTheme] = useState<string>();

  // Local storage
  useEffect(() => {
    if (typeof window !== "undefined" && currentTheme) {
      localStorage.setItem("theme", currentTheme);
    }
  }, [currentTheme]);

  useEffect(() => {
    setCurrentTheme(localStorage.getItem("theme") || Theme.Light);
  }, []);

  const siwtchTheme = () => {
    currentTheme === Theme.Light
      ? setCurrentTheme(Theme.Dark)
      : setCurrentTheme(Theme.Light);
  };
2
  • As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
    – Community Bot
    Commented Oct 8, 2023 at 11:14
  • That's a lotta useEffect()!
    – Raydot
    Commented Mar 26 at 21:58
0

I have created a function getLocalStorageItem and called this in useEffect with the required key name. After getting the value from localStorage, saved it in a state(i.e currentUser) and used it in initialState.

 const [currentUser, setCurrentUser] = useState({});

  const getLocalStorageItem = (key) => {
    return typeof window !== undefined
      ? window.localStorage.getItem(key)
      : null;
  };

  useEffect(() => {
    setCurrentUser({
      token: getLocalStorageItem("token"),
      refreshToken: getLocalStorageItem("refreshToken"),
    });
  }, []);

  const initialState = {
    auth: {
      isLoggedIn: true,
      currentUser: currentUser,
    },
  };
0

localStorage is only available client side. So Next.js will throw an error if you attempt to access localStorage to getItem or setItem.

Instead, inside the component, use a useEffect hook to access the localStorage on first client side render.

useEffect(() => {
  localStorage.setItem("abc", "def");
}, []);

Not the answer you're looking for? Browse other questions tagged or ask your own question.