55

I am trying to make my website SEO friendly with meta tags. I am implementing server-side rendering in my application. After this, I am getting the following error:

ReferenceError: localStorage is not defined.

Please help how to resolve it.

My package.json:

{


 "main": "server.js",
 "scripts": {
  "start-dev": "NODE_ENV=development webpack -w & NODE_ENV=development node server.js",
  "test": "echo \"Error: no test specified\" && exit 1"
 },
 "keywords": [],
 "author": "",
 "license": "ISC",
 "dependencies": {
  "axios": "^0.18.0",
  "express": "^4.15.3",
  "firebase": "^4.12.1",
  "html2canvas": "^1.0.0-alpha.12",
  "react": "^16.2.0",
  "react-adsense": "0.0.5",
  "react-dom": "^16.2.0",
  "react-facebook-login": "^4.0.1",
  "react-google-login": "^3.2.1",
  "react-meta-tags": "^0.3.0",
  "react-router-dom": "^4.2.2",
  "react-router-match-as-promised": "^1.0.5",
  "react-scripts": "1.1.1",
  "react-share": "^2.1.1",
  "react-slick": "^0.22.3"
 },
 "devDependencies": {
  "autoprefixer": "^7.1.2",
  "babel-core": "^6.25.0",
  "babel-loader": "^7.1.1",
  "babel-preset-es2015": "^6.24.1",
  "babel-preset-react-app": "^3.1.2",
  "babel-preset-stage-0": "^6.24.1",
  "css-loader": "^0.28.4",
  "extract-text-webpack-plugin": "^2.1.2",
  "file-loader": "^0.11.2",
  "postcss-loader": "^2.0.6",
  "webpack": "^3.1.0",
  "webpack-node-externals": "^1.7.2"
 }
}

Meta Tags commonly used in all pages. Home.js

<code>
import React, { Component } from 'react';
import axinst from '../common';
import {TabBox,TabBox2,TabBox3} from '../common/tabbox';
import Ads  from '../common/ads';
import SubscribeFrm from '../common/subscribefrm';
import MetaTags from 'react-meta-tags';
import AdSense from 'react-adsense';
import Loader from '../common/loader';

class Home extends Component {
  constructor(props) {
    super(props);
  }

  state = {
    header:[],
    otherSports:[],
    wizztalk:[],
    sports:[],
    isProgress:'',
    activeCat: '',
    metaTitle:'',
    metaDesc:'',
    metaKey:''
  }

  componentDidMount(){


    // axinst.get('/home')
    this.setState({isProgress:true});
    axinst.get('/home').then(response => {
      this.setState({isProgress:false});
      const header = response.data.data.Header;
      const sports = response.data.data.Sports;
      const otherSports = response.data.data.OtherSports;
      const wizztalk = response.data.data.Wizztalk;
      const metaTitle = response.data.data.metaTitle;
      const metaDesc = response.data.data.metaDesc;
      const metaKey = response.data.data.metaKeyword;
      this.setState({header});
      this.setState({sports});
      this.setState({otherSports})
      this.setState({wizztalk});
      this.setState({metaTitle});
      this.setState({metaDesc});
      this.setState({metaKey});
    }).catch(function (error) {
      // console.log(error);
      // console.log('error');
    });
  }

  render() {
    const hD = this.state.header;
    const sport = this.state.sports;
    return (
    <div id="maincontent">
        <MetaTags>
          <title>{this.state.metaTitle}</title>
          <meta name="title" content={this.state.metaTitle} />
          <meta name="keywords" content={this.state.metaKeyword} />
          <meta name="description" content={this.state.metaDesc} />
          <meta name="og:description" content={this.state.metaDesc} />
          <meta name="og:title" content={this.state.metaTitle} />
          <meta name="og:image" content={process.env.PUBLIC_URL +"/images/logo.png"}/>
        </MetaTags>
</code>
Patrick Hund
  • 19,163
  • 11
  • 66
  • 95
Chander Shoor
  • 566
  • 1
  • 4
  • 8

7 Answers7

86

When you're rendering on the server, you do not have a browser and thus you do not have access to all the APIs that the browser provides, including localStorage.

In JavaScript code that is running both on the server and on the client (browser), it is common practice to guard against with an if clause that checks if window is defined. “Window” is the root object provided by the browser for all the APIs that are provided by the browser.

Example:

if (typeof window !== 'undefined') {
    console.log('we are running on the client')
} else {
    console.log('we are running on the server');
}

In your case, you want to put your call to localStorage in such an if clause, for example:

if (typeof window !== 'undefined') {
    localStorage.setItem('myCat', 'Tom');
}
Patrick Hund
  • 19,163
  • 11
  • 66
  • 95
  • The code in your question does not use localStorage anywhere. Perhaps it is used in one of the packages you're importing, possibly *react-adsense*. But you didn't include the part where you actually use that, so I cannot help you further. – Patrick Hund Sep 24 '18 at 07:32
  • can you please guide me how to use localstorage in my application. or send any useful link if you have any. – Chander Shoor Sep 24 '18 at 07:36
  • 1
    I already did, or tried to: wherever you use local storage, check if it's actually available with an if clause like I showed in my code examples – Patrick Hund Sep 24 '18 at 07:37
  • I have used your code, but still getting same error. – Chander Shoor Sep 24 '18 at 07:43
  • As @PatrickHund said you might want to check the code/docs of your dependencies to check which package is using `localStorage` and replace it with a _SSR friendly_ package – t3__rry Sep 24 '18 at 08:00
16

This oneliner did it for me:

const checkout = typeof window !== 'undefined' ? localStorage.getItem('checkout') : null
4

You can add a polyfill to your app. I.e. somewhere in your index.js:

if (!window) {
    require('localstorage-polyfill');
}
Alejandro
  • 5,834
  • 1
  • 19
  • 36
  • After using this code my whole code is break. --------------------- Following error is coming. Warning: Invalid DOM property `class`. Did you mean `className`? – Chander Shoor Sep 24 '18 at 08:47
  • 1
    It's not clear how it could be linked, however yes, react uses `className` attribute instead of `class`. – Alejandro Sep 24 '18 at 08:51
  • I have used this package for metatags. https://www.npmjs.com/package/react-meta-tags. i want to make it seo friendly. – Chander Shoor Sep 24 '18 at 08:57
  • it's already different issue, so I'm assuming the one you asked for help already fixed. For the last issue you've asked - just replace `class` with `className`, it should help. However, I'd note that moving everything to server side rendering - is not a simple task, you'll probably faced with other issues as well. I'd suggest you to ask new specific questions for that (of course if they hasn't been asked before). I don't think somebody here can fix everything in your app in one answer. – Alejandro Sep 24 '18 at 09:05
1

In my case I used a useEffect to wait for the page to load before using localStorage.

Shane Sepac
  • 806
  • 10
  • 20
1

On Component you can use useEffect hook for waiting the full page load.

    useEffect(() => {
      localStorage.getItem('key');
    }, [input]);

And for hooks or functions you can use if condition to check window is loaded or not like this -

    if(window !== 'undefined'){ 
      localStorage.getItem('key')
    }
0

This is a more reliable approach if the component logic absolutely depends on the value of lsVar to conditionally render something.

    const Component = () => {
        if (typeof window === "undefined") return null;

        const lsVar = localStorage.getItem("lsVar");

        // component logic
        
        return (<></>);
    }
0

I faced this issue with NextJs while checking the query param values for session creation.

The best working solution was :

if (typeof window !== 'undefined') {
    console.log('Currently on Client side')
} else {
    console.log('Currently on Server Side');
}

Inside of the first check (Client side), simply put all the code to handle localStorage. This works for me fine.