Beyond cookies: Today’s options for client-side data storage
Tags: featuredposts
Published: 2019-07-31 13:38:40

---
title: Beyond cookies: Today’s options for client-side data storage
published: true
tags: Featured Posts
canonical_url: https://blog.logrocket.com/beyond-cookies-todays-options-for-client-side-data-storage/
---

![](https://thepracticaldev.s3.amazonaws.com/i/sge16nclrm5k8cids8py.jpg)

When they were first introduced, cookies were the only way for a browser to save data. Since then, there have been new options added — the Web Storage API, IndexedDB, and the Cache API among them. So, are cookies dead? Let’s take a look at each of the options for storing data in your browser.

## Cookies

Cookies are bits of information either sent by the server or set on the client that are saved locally on the user’s browser. They are automatically attached to every request. Since HTTP is a stateless protocol, cookies allow for information to be stored on the client in order to pass additional context to that server.

Cookies have a few flags that can be very useful for increasing the security of your app’s data. The `HttpOnly` flag prevents a cookie from being accessed using JavaScript; they are only accessible when being attached on HTTP requests. This is great for preventing the exposure of your data through XSS (cross-site scripting) attacks.

In addition, the `Secure` flag ensures that a cookie is only sent when the request is sent over the HTTPS protocol. The `SameSite` flag, which can be set to `lax` or `strict` (read about the difference [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#SameSite_cookies)), can be used to help prevent against CSRF (cross-site request forgery) requests. It tells the browser to only send the cookies if the request is to a URL on the same domain as the requester.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

### When would you use cookies?

So, what are some cases in which you might want to reach for cookies? One of the most common use cases is for authorization tokens. Since the `HttpOnly` flag adds an extra layer of protection against XSS attacks, `SameSite` can prevent against CSRF, and `Secure` can ensure that your cookie is encrypted, your auth token has an extra layer of protection.

Since auth tokens are quite small, you don’t need to worry about each request being bloated in size. In addition, since they are automatically attached to every request, using cookies allows you to determine on the server if the user is authenticated. This can be great for server-rendered content or if you would like to redirect a user to the login page if they are not authenticated.

Another good use of cookies is for storing your user’s language code. Since you are likely to want access to the user’s language on most requests, you can take advantage of the fact that it is automatically attached.

### How would you use cookies?

Now that we have discussed why you might want to use cookies, let’s take a look at _how_ you can use cookies. To set a cookie on the client from the server, add a `Set-Cookie` header in the HTTP response. The cookies should be in the format of `key=value`. For example, if you were setting cookies from a Node.js application, your code might look like this:

```jsx
response.setHeader('Set-Cookie', ['user_lang=en-us', 'user_theme=dark_mode']);
```

This will set two cookies: it will set `user_lang` to `en-us` and `user_theme` to `dark_mode`.

Cookies can also be manipulated by the client. To set a cookie, you can assign a value to `document.cookie` in the format of `key=value`. If the key already exists, it will be overwritten.

```jsx
document.cookie = 'user_lang=es-es';
```

If `user_lang` had already been defined, it will now be equal to `es-es`.

You can read all the cookies by accessing the `document.cookie` value. This will return a string of semicolon-separated key/value pairs.

```jsx
document.cookie = 'user_lang=en-us';
document.cookie = 'user_theme=light_mode';
console.log(document.cookie); // 'user_lang=en-us; user_theme=light_mode;'
```

To increase the accessibility of the key/value pairs, you can parse this string into an object with the following function:

```jsx
const parseCookies = x => x
  .split(';')
  .map(e => e.trim().split('='))
  .reduce((obj, [key, value]) => ({...obj, [key]: value}), {});
```

If you need to set one of the flags onto your cookie, you can add them after a semicolon. For example, if you’d like to set the `Secure` and `SameSite` flags onto your cookie, you would do the following:

```jsx
document.cookie = 'product_ids=123,321;secure;samesite=lax'
```

Since `HTTPOnly` is designed to make a cookie accessible only on the server, it can _only_ be added by the server.

In addition to these security flags, you can set either a `Max-Age` (the number of seconds that a cookie should last) or an `Expires` (the date at which the cookie should be expired). If neither of these is set, the cookie will last for the duration of the browser’s session. If the user is using incognito, the cookies will be removed when the user’s session is closed.

Since the interface for dealing with cookies isn’t the most friendly, you may want to use a utility library such as `js-cookie` for ease of use.

## Web Storage API

A newer option to store data locally is the Web Storage API. Added in HTML5, the Web Storage API includes `localStorage` and `sessionStorage`. While cookies typically deal with server/client communication, the Web Storage API is best used for client-only data.

Since we already had cookies as an option to store data locally, why is Web Storage necessary? One reason we already touched on: since cookies are automatically added to each HTTP request, request sizes can get bloated. Due to this, you can store larger amounts of data using the Web Storage API than you can with cookies.

Another advantage is the more intuitive API. With cookies, you would need to manually parse the cookie string in order to access individual keys. Web Storage makes this easier. If you would like to set or get a value, you can run `setItem` or `getItem`.

```jsx
localStorage.setItem('selected_tab', 'FAQ');
localSTorage.getItem('selected_tab'); // 'FAQ'
```

Both the key and value _must_ be strings; if you would like to save an object or array, you can do this by calling `JSON.stringify()` while saving and `JSON.parse()` while reading.

```jsx
const product = {
  id: '123',
  name: 'Coffee Beans',
};

localStorage.setItem('cached_product', JSON.stringify(product));
JSON.parse(localStorage.getItem('cached_product'));
```

Another use case for local storage is to sync up data between multiple tabs. By adding a listener for the `'storage'` event, you can update data in another tab/window.

```jsx
window.addEventListener('storage', () => {
  console.log('local storage has been updated');
});
```

This event will be triggered only when local or session storage has been modified _in another document_ — that is, you cannot listen for storage changes within your current browser tab. Unfortunately, as of the writing of this article, the storage event listener [does not yet work on Chrome](https://developer.mozilla.org/en-US/docs/Web/API/Window/storage_event#Browser_compatibility).

So, what are the differences between `localStorage` and `sessionStorage`? Unlike with cookies, there is no expiration or max-age feature for the Web Storage API. If you use `localStorage`, the data will last indefinitely unless it is manually deleted. You can remove the value of a single key by running `localStorage.removeItem('key')`, or you can clear _all_ of the data by running `localStorage.clear()`.

If you use `sessionStorage`, the data will only last for the current session. It will be treated similarly to how a cookie will persist if you do not set a max-age or expiration. In either case, if the user is incognito, the local storage will not persist between sessions.

## IndexedDB

If neither cookies nor `localStorage` seem like the right fit, there is another alternative: IndexedDB, an in-browser database system.

While `localStorage` performs all of its methods synchronously, IndexedDB calls them all asynchronously. This allows the accessing of the data without blocking the rest of your code. This is great when you are dealing with larger amounts of code that could be expensive to access.

IndexedDB also has more flexibility in the type of data that it stores. While cookies and `localStorage` are limited to only storing strings, IndexedDB can store any type of data that can be copied by the “structured clone algorithm.” This includes objects with a type of `Object`, `Date`, `File`, `Blob`, `RegEx`, [and many more](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#Supported_types).

The downside to this increase in performance and flexibility is that the API for IndexedDB is much more low-level and complicated. Luckily, there are many utility libraries that can help with this.

`localForage` gives a simpler, `localStorage`-like API to IndexedDB. [PouchDB](https://pouchdb.com/) gives an offline-ready storage API that can sync with an online [CouchDB](http://couchdb.apache.org/) database. [idb](https://github.com/jakearchibald/idb) is a tiny library with a much simpler promise-based API. [Dexie](https://dexie.org/) adds a much more robust query API while maintaining good performance. Depending on your use, there are many options available.

## Cache API

Another specialized tool for persistent data is the Cache API. Although it was originally created for service workers, it can be used to cache any network requests. The Cache API exposes `Window.caches`, which provides methods for saving and retrieving responses. This allows you to save pairs of `Requests` and `Responses` that you can later access.

For example, if you would like to check the browser’s cache for a response before requesting it from an API, you can do the following:

```jsx
const apiRequest = new Request('https://www.example.com/items');
caches.open('exampleCache') // opens the cache
  .then(cache => {
    cache.match(apiRequest) // checks if the request is cached
      .then(cachedResponse => 
        cachedResponse || // return cachedReponse if available
        fetch(apiRequest) // otherwise, make new request
          .then(response => {
            cache.put(apiRequest, response); // cache the response
            return response;
          })
        })
    .then(res => console.log(res))
```

The first time that the code is run, it will cache the response. Each subsequent time, the request is cached, and no network request is made.

## In conclusion

Each method of storing data on the browser has its own use. If the information is small, sensitive, and likely to be used on the server, cookies are the way to go. If you are saving data that is larger and less sensitive, the Web Storage API may be a better pick.

IndexedDB is great if you are planning on storing large amounts of structured data. The Cache API is used for storing responses from HTTP requests. Depending on what you need, there are plenty of tools for the job.

## Additional resources and further reading

You can read the MDN web docs for more info on the methods discussed above:

- [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API)
- [HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)
- [IndexedDB API](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)
- [Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache)

* * *
## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *
The post [Beyond cookies: Today’s options for client-side data storage](https://blog.logrocket.com/beyond-cookies-todays-options-for-client-side-data-storage/) appeared first on [LogRocket Blog](https://blog.logrocket.com).