localStorage

We store data in the user's browser indefinitely (almost).

Time to read: less than 5 min

Briefly

This is an object stored in window that allows you to persistently save data in the browser. It works as a key-value data storage — when saving data, we specify the field name where the data should be saved and then use this name to retrieve it.

Values are stored as strings. When attempting to save other types of data, they will be converted to a string. For example, if you write a number, then when reading it back, you will get the number stored as a string.

There are no time storage limitations, but it can be cleared manually by the user or automatically by the browser when full (WebKit-based browsers, like Safari, clear localStorage if it hasn't been accessed for 7 days).

The maximum data size is limited to 5MB.

Example

Storing data:

        
          
          window.localStorage.setItem('name', 'WebGuide Dog')
          window.localStorage.setItem('name', 'WebGuide Dog')

        
        
          
        
      

When reading previously stored data by the key name, we get WebGuide Dog:

        
          
          const name = window.localStorage.getItem('name')console.log(name)// 'WebGuide Dog'
          const name = window.localStorage.getItem('name')
console.log(name)
// 'WebGuide Dog'

        
        
          
        
      

Re-writing with the same key will replace the data:

        
          
          window.localStorage.setItem('name', 'Dog WebGuide')const name = window.localStorage.getItem('name')console.log(name)// 'Dog WebGuide'
          window.localStorage.setItem('name', 'Dog WebGuide')

const name = window.localStorage.getItem('name')
console.log(name)
// 'Dog WebGuide'

        
        
          
        
      

How to understand

If you need to store data in the browser for a long time and the volume of this data is large enough, then localStorage is what you need. The data will be stored indefinitely and can only be deleted in two cases: when exceeding the data size limit or when the storage is cleared by the user or programmatically.

How to write

Writing

To write, use the method setItem('key', 'value'). It takes two string parameters: the key under which the value will be saved and the value itself.

        
          
          window.localStorage.setItem('name', 'WebGuide Dog')
          window.localStorage.setItem('name', 'WebGuide Dog')

        
        
          
        
      

Reading

Reading is done with getItem('key'), which has one parameter that specifies the key for reading and returns the retrieved value from the storage. If there is no value for this key, the method will return null.

        
          
          window.localStorage.getItem('name') // will return 'WebGuide Dog'window.localStorage.getItem('user') // will return `null`
          window.localStorage.getItem('name') // will return 'WebGuide Dog'
window.localStorage.getItem('user') // will return `null`

        
        
          
        
      

Deleting

Removes the entry from the storage removeItem('key'). It will succeed even if the specified key does not exist in the storage.

        
          
          window.localStorage.removeItem('name')window.localStorage.removeItem('user')
          window.localStorage.removeItem('name')
window.localStorage.removeItem('user')

        
        
          
        
      

Clearing the storage

The method clear() clears the storage completely.

        
          
          window.localStorage.clear()
          window.localStorage.clear()

        
        
          
        
      

Number of fields in the storage

Using the length property, you can find out how many fields have been recorded in the storage.

        
          
          console.log(window.localStorage.length)// 0
          console.log(window.localStorage.length)
// 0

        
        
          
        
      

Getting the key by index

The method key() gets the key by index. Values in the storage are stored in the order they were added, so the value added first will be at position 0 and so on.

        
          
          window.localStorage.setItem('name', 'WebGuide Dog')console.log(window.localStorage.key(0))// 'name'
          window.localStorage.setItem('name', 'WebGuide Dog')
console.log(window.localStorage.key(0))
// 'name'

        
        
          
        
      

Thus, by using the number of fields in the storage and getting the key by index, you can organize the iteration over all values in the storage.

        
          
          const localStorageSize = window.localStorage.lengthfor (let i = 0; i < localStorageSize; i++) {  console.log(    window.localStorage.getItem(localStorage.key(i))  )}
          const localStorageSize = window.localStorage.length
for (let i = 0; i < localStorageSize; i++) {
  console.log(
    window.localStorage.getItem(localStorage.key(i))
  )
}

        
        
          
        
      

Events

When setting a value in the storage, a global storage event is triggered, which can be used to track changes in the storage.

💡 The event occurs only on other open pages of the current site.

The event contains properties:

  • key — the key that was changed (when the clear() method is called, the key will be null);
  • oldValue — the old value recorded in the field;
  • newValue — the new value recorded in the field;
  • url — the address of the page where the change was made.
        
          
          window.addEventListener('storage', function (evt) {  console.log(evt)})
          window.addEventListener('storage', function (evt) {
  console.log(evt)
})

        
        
          
        
      

In practice

Advice 1

🛠 Using localStorage, you can save data related to the user without storing it on the server. In the following example, we will remember the font size on the website and restore the size from storage if it was changed beforehand.

Open demo in the new window

🛠 It can be used to synchronize multiple tabs open in the browser. When the font size is changed in one tab, we learn about it in all the others and change the size as well.

        
          
          function changePageFontSize(size) {  document.style.fontSize = `${size}px`}window.addEventListener('storage', function (evt) {  if (evt.key === 'pageFontSize') {    changePageFontSize(evt.newValue)  }})
          function changePageFontSize(size) {
  document.style.fontSize = `${size}px`
}

window.addEventListener('storage', function (evt) {
  if (evt.key === 'pageFontSize') {
    changePageFontSize(evt.newValue)
  }
})

        
        
          
        
      

🛠 Sometimes we need to save not just text but a whole data structure. This is where JSON.stringify() comes in handy.

        
          
          const user = {  name: 'WebGuide Dog',  avatarUrl: 'mascot-doka.svg'}localStorage.setItem('user', JSON.stringify(user))
          const user = {
  name: 'WebGuide Dog',
  avatarUrl: 'mascot-doka.svg'
}

localStorage.setItem('user', JSON.stringify(user))

        
        
          
        
      

And after reading we parse:

        
          
          function readUser() {  const userJSON = localStorage.getItem('user')  if (userJSON === null) {    return undefined  }  // If for some reason the storage contains an invalid JSON,  // we protect ourselves from this  try {    return JSON.parse(userJSON)  } catch (e) {    localStorage.removeItem('user')    return undefined  }}console.log(readUser())// {//  name: 'WebGuide Dog',//  avatarUrl: 'mascot-doka.svg'// }
          function readUser() {
  const userJSON = localStorage.getItem('user')

  if (userJSON === null) {
    return undefined
  }

  // If for some reason the storage contains an invalid JSON,
  // we protect ourselves from this
  try {
    return JSON.parse(userJSON)
  } catch (e) {
    localStorage.removeItem('user')
    return undefined
  }
}

console.log(readUser())
// {
//  name: 'WebGuide Dog',
//  avatarUrl: 'mascot-doka.svg'
// }

        
        
          
        
      

🛠 If your site uses analytics scripts or other external libraries, they will also have access to the storage. Therefore, it's better to name the keys for storing with a prefix in a consistent style. For example, when writing anything on such a site, I would choose the prefix YD_{key name}, so that only the necessary values can be grouped or filtered in the developer tools.

🛠 Use wrapper functions to prevent errors related to failed write attempts, the absence of localStorage in the browser, and code duplication.

        
          
          function getItem(key, value) {  try {    return window.localStorage.getItem(key)  } catch (e) {    console.log(e)  }}function setItem(key, value) {  try {    return window.localStorage.setItem(key, value)  } catch (e) {    console.log(e)  }}function setJSON(key, value) {  try {    const json = JSON.stringify(value)    setItem(key, json)  } catch (e) {    console.error(e)  }}function getJSON(key) {  try {    const json = getItem(key)    return JSON.parse(json)  } catch (e) {    console.error(e)  }}
          function getItem(key, value) {
  try {
    return window.localStorage.getItem(key)
  } catch (e) {
    console.log(e)
  }
}

function setItem(key, value) {
  try {
    return window.localStorage.setItem(key, value)
  } catch (e) {
    console.log(e)
  }
}

function setJSON(key, value) {
  try {
    const json = JSON.stringify(value)

    setItem(key, json)
  } catch (e) {
    console.error(e)
  }
}

function getJSON(key) {
  try {
    const json = getItem(key)

    return JSON.parse(json)
  } catch (e) {
    console.error(e)
  }
}

        
        
          
        
      

Advice 2

        
          
          🛠 How to prevent data deletion from storage?When and how does data deletion occur? How can we retain data?You can use two methods of data storage - "temporary" and "permanent." In "temporary" mode, the browser can delete your data, provided that this process does not interfere with the user's experience. In permanent mode, this will not happen, even in case of low storage space. However, users can independently clear the storage in this mode.By default, all storage (`localStorage`, `IndexedDB`, `Cache API`, etc.) operates in temporary mode. If the site did not [request permanent data storage](https://web.dev/persistent-storage/) — the browser can delete its data at its discretion. For example, if there is little space on the device.🛠 What does the cleanup process look like?During automatic cleanup, browsers should delete the least valuable data for the user.Chromium-based browsers free up space sequentially by deleting data from sites. Deletion starts with sites that have not been visited for a long time and continues until enough space is freed.Internet Explorer 10+ does not clear data but does not allow data to be written beyond the limit.Firefox starts clearing when the free disk space runs out. The order of deletion is determined by the same principle as in Chromium-based browsers.Safari used to never delete data, but now uses a [seven-day limit on storing all recorded data](https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/).🛠 How to enable permanent data storage mode?All the necessary methods for working with browser storage are found in `window.navigator.storage`. The method we need is called `persist()`:```js(async () => {  // Attempts to switch to "permanent" storage mode.  // Returns true if successful, false if not.  const persistModeEnabled = await window.navigator.storage?.persist()})()
          🛠 How to prevent data deletion from storage?

When and how does data deletion occur? How can we retain data?

You can use two methods of data storage - "temporary" and "permanent." In "temporary" mode, the browser can delete your data, provided that this process does not interfere with the user's experience. In permanent mode, this will not happen, even in case of low storage space. However, users can independently clear the storage in this mode.

By default, all storage (`localStorage`, `IndexedDB`, `Cache API`, etc.) operates in temporary mode. If the site did not [request permanent data storage](https://web.dev/persistent-storage/) — the browser can delete its data at its discretion. For example, if there is little space on the device.

🛠 What does the cleanup process look like?

During automatic cleanup, browsers should delete the least valuable data for the user.

Chromium-based browsers free up space sequentially by deleting data from sites. Deletion starts with sites that have not been visited for a long time and continues until enough space is freed.

Internet Explorer 10+ does not clear data but does not allow data to be written beyond the limit.

Firefox starts clearing when the free disk space runs out. The order of deletion is determined by the same principle as in Chromium-based browsers.

Safari used to never delete data, but now uses a [seven-day limit on storing all recorded data](https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/).

🛠 How to enable permanent data storage mode?

All the necessary methods for working with browser storage are found in `window.navigator.storage`. The method we need is called `persist()`:

```js
(async () => {
  // Attempts to switch to "permanent" storage mode.
  // Returns true if successful, false if not.
  const persistModeEnabled = await window.navigator.storage?.persist()
})()

        
        
          
        
      

We can enhance our code by using the persisted() method. This method will return true if the storage has already been switched to permanent mode.

We call the persisted() method and switch the storage to permanent mode only if it has not been switched yet:

        
          
          (async () => {  // boolean value indicating whether  // the "permanent" data storage mode is enabled initially  const isAlreadyPersist = await window.navigator.storage?.persisted()  if (isAlreadyPersist) {    console.info('The storage has already been switched to permanent storage mode.')    return  }  // boolean value indicating whether  // it was possible to switch to "permanent" storage mode  const persistModeEnabled = await window.navigator.storage?.persist()  if (persistModeEnabled) {    console.info('The browser successfully changed the storage mode to "permanent".')    return  }  console.info(    'The browser encountered issues when trying to change the mode. You may want to update to the latest version or use HTTPS protocol on the site.'  )})()
          (async () => {
  // boolean value indicating whether
  // the "permanent" data storage mode is enabled initially
  const isAlreadyPersist = await window.navigator.storage?.persisted()

  if (isAlreadyPersist) {
    console.info('The storage has already been switched to permanent storage mode.')
    return
  }

  // boolean value indicating whether
  // it was possible to switch to "permanent" storage mode
  const persistModeEnabled = await window.navigator.storage?.persist()

  if (persistModeEnabled) {
    console.info('The browser successfully changed the storage mode to "permanent".')
    return
  }

  console.info(
    'The browser encountered issues when trying to change the mode. You may want to update to the latest version or use HTTPS protocol on the site.'
  )
})()