Session Storage and Local Storage in React

After reading the Road to learn React, a few readers approached me with a question: How can I persist state in React? Obviously it would be possible by having a backend to persist it in a database. Once the app starts, the React app would make a request to the backend to retrieve the state. Then it could be stored in the local component state or in a state container of a state management library like Redux or MobX.

But a simpler yet most of the times sufficient solution could be to use the native local storage of the browser. There is no backend and no additional library needed. The article gives you a little showcase on how to persist state in React with local storage, how to use it as a cache and how to make it expire.

Initiating the React App for Local Storage Example

You can use create-react-app or any other React boilerplate. It is recommended to use create-react-app.

In the following example you will fetch data from an API and store it in React’s local state. Furthermore, you will use the local storage to store the result in the browser. So whenever you closer your browser and open it again, the fetched result from the API should be still available.

After you have set up your project with create-react-app, you can replace your root component with the following boilerplate.

import React from 'react';

class App extends React.Component {

  constructor(props) {
    super(props);
    this.state = { hits: null };
  }

  onSearch = (e) => {
    e.preventDefault();

    const { value } = this.input;

    if (value === '') {
      return;
    }

    fetch('https://hn.algolia.com/api/v1/search?query=' + value)
      .then(response => response.json())
      .then(result => this.onSetResult(result));
  }

  onSetResult = (result) => {
    this.setState({ hits: result.hits });
  }

  render() {
    return (
      <div>
        <h1>Search Hacker News with Local Storage</h1>
        <p>
          There shouldn't be a second network request,
          when you search for something twice.
        </p>

        <form type="submit" onSubmit={this.onSearch}>
          <input type="text" ref={node => this.input = node} />
          <button type="button">Search</button>
        </form>

        {
          this.state.hits &&
          this.state.hits.map(item => <div key={item.objectID}>{item.title}</div>)
        }
      </div>
    );
  }
}

export default App;

If you understand everything that is happening here, you can head over to the next headline to add the local storage as feature. Otherwise you can read the brief recap:

In this sample application, you can experience the unidirectional data flow in React. The onSubmit handler of the form is used to execute the class method onSearch(). The onSearch() method will do nothing, if the input field was empty. If it wasn’t empty, it will perform a request to the Hacker News API. After the request was successful, the method stores the result in the local component state by using React setState(). Read the following article to read more about data fetching in React.

Furthermore, in the render class method, you will find a form by using the ref attribute and a conditional rendering for the fetched list from the API. The former is used to submit your search request to an API by having access to the input field’s value. The latter will make sure that there is either a list or nothing at all rendered by your component when there is no result.

The onSearch class method uses the native fetch API of the browser to retrieve stories from the Hacker News platform. You could also use a third-party library like axios or superagent to perform such a request. That’s one of the things that makes React so powerful in the first place, because you stay flexible in choosing your building blocks.

Introduction to Local Storage

The local storage is supported by most of the browsers. You can check the browser compatibility and read even more about the topic in the official documentation. The usage of the local storage is fairly straight forward. In your JavaScript code, running in the browser, you should have access to the localStorage object. The object has a setter and getter to store and retrieve data from the object.

// setter
localStorage.setItem('myData', data);

// getter
localStorage.getItem('myData');

Once you close the browser and open the app again, you will find the data still in the object.

Local Storage in React (as Cache)

Now you can add only a few lines to enable caching in your application. Even if you close the browser and open the application again, you will have a cached result which was from the API.

import React from 'react';

class App extends React.Component {

  constructor(props) {
    super(props);
    this.state = { hits: null };
  }

  onSearch = (e) => {
    e.preventDefault();

    const { value } = this.input;

    if (value === '') {
      return;
    }

    const cachedHits = localStorage.getItem(value);
    if (cachedHits) {
      this.setState({ hits: JSON.parse(cachedHits) });
      return;
    }

    fetch('https://hn.algolia.com/api/v1/search?query=' + value)
      .then(response => response.json())
      .then(result => this.onSetResult(result, value));
  }

  onSetResult = (result, key) => {
    localStorage.setItem(key, JSON.stringify(result.hits));
    this.setState({ hits: result.hits });
  }

  render() {
    return (
      <div>
        <h1>Search Hacker News with Local Storage</h1>
        <p>
          There shouldn't be a second network request,
          when you search for something twice.
        </p>

        <form type="submit" onSubmit={this.onSearch}>
          <input type="text" ref={node => this.input = node} />
          <button type="button">Search</button>
        </form>

        {
          this.state.hits &&
          this.state.hits.map(item => <div key={item.objectID}>{item.title}</div>)
        }
      </div>
    );
  }
}

export default App;

There shouldn’t be a request to the API made twice for the same search term, because the result should be cached in the local storage. If there are cachedHits in the localStorage object, the method returns earlier without performing a request. The result in the cache is an JavaScript object and thus needs to be stringified when stored and parsed when retrieved from the storage.

Expiration with Session Storage

Sometimes you want the cache only in your current session. When closing the browser, you want the cache to become empty again. That’s where you can use the native sessionStorage instead of the localStorage. The session storage is used in the same way as the local storage:

// setter
sessionStorage.setItem('myData', data);

// getter
sessionStorage.getItem('myData');

It can be useful when you deal with an user session after a user logged in into your application. The login session could be saved until the browser is closed.


You can find the source code in a GitHub repository. If you like it, make sure to star it. As mentioned, sometimes you don’t need a sophisticated persistent layer in your application. It turns out to be sufficient to use the local storage or session storage as a cache.

If you are looking for more advanced local storage solutions, you can checkout store.js and cross-storage. The former is used for browser compatibility and the latter is used for cross domain synchronization of local storages.

The book the Road to learn React will show you how to implement a cache with paginated data by using setState() and this.state. After reading it, you could apply this tutorial to have a cached state even after the browser is closed.

Build a Hacker News App along the way. No setup configuration. No tooling. No Redux. Plain React in 190+ pages of learning material. Learn React like 13.000+ readers.

Get the Book
comments powered by Disqus

Never miss an article about web development and self-growth.

Take Part

Join 7800+ Developers

Learn Web Development with JavaScript

Tips and Tricks

Access Tutorials, eBooks and Courses

Personal Development as a Software Engineer