Framework
Storage

Storage API

See LicenseNPM InstallFollow PlasmoHQ on TwitterWatch our Live DEMO every FridayJoin our Discord for support and chat about our projects

@plasmohq/storage is a utility library from plasmo (opens in a new tab) that abstracts the persistent storage API available to browser extensions. It falls back to localstorage when the extension storage API is unavailable, allowing for state sync between popup - options - contents - background.

This library will enable the storage permission automatically if used with the Plasmo framework (opens in a new tab)

Installation

pnpm install @plasmohq/storage

Usage Examples

Storage API (for Non-React Components)

The base Storage API is designed to be easy to use. Get/set data without the need to JSON.stringify/parse. As long as the data you are storing is a plain object or a primitive type, it can be stored:

import { Storage } from "@plasmohq/storage"
 
const storage = new Storage()
 
await storage.set("key", "value")
 
const data = await storage.get("key") // "value"
 
await storage.set("capt", { color: "red" })
 
const data2 = await storage.get("capt") // { color: "red" }

Customizing the storage area

const storage = new Storage({
  area: "local"
})

Preventing data from being mirrored into localStorage

const storage = new Storage({
  secretKeyList: ["shield-modulation"]
})

The code above prevents data from being leaked into localStorage when used with content scripts. This setting is recommended if you are storing JWT access token to invoke your server-side API.

Watch (for state sync)

To watch for changes when using the Storage API:

background.ts
import { Storage } from "@plasmohq/storage"
 
const storage = new Storage()
 
await storage.set("serial-number", 47)
await storage.set("make", "plasmo-corp")
 
storage.watch({
  "serial-number": (c) => {
    console.log(c.newValue)
  },
  make: (c) => {
    console.log(c.newValue)
  }
})
 
await storage.set("serial-number", 96)
await storage.set("make", "PlasmoHQ")

This can be used as a layer to communicate messages across your extension. We demonstrate this in the with-redux (opens in a new tab) example.

Hook API (for React components)

The hook API is designed to streamline the state-syncing workflow between the different pieces of an extension. There are many ways it can be used, but first and foremost you will want to import the hook into your React component:

import { useStorage } from "@plasmohq/storage/hook"

Watch and render a value in storage:

const [hailingFrequency] = useStorage("hailing")
...
{hailingFrequency}

Specifying the storage area:

const [hailingFrequency] = useStorage({
  key: "hailing",
  area: "local"
})

Specifying an initial value for rendering WITHOUT persisting

"Persisting" means writing into the internal memory. By not persisting the value, only this specific instance of the hook will render the given initial value when there is no value in storage. Other instances can either show undefined OR specify their own initial value. To elaborate on this:

Given a popup.tsx that sets a static initial value:

popup.tsx
const [hailingFrequency, setHailingFrequency] = useStorage("hailing", "42")
 
return <input value={hailingFrequency} onChange={(e) => setHailingFrequency(e.target.value)}/> // "42"

If we subscribe to this key in a content.tsx script, we will see it be undefined until setHailingFrequency is called with a defined value:

content.tsx
const [hailingFrequency] = useStorage("hailing")
 
return <p>{hailingFrequency}</p> // undefined

If we subscribe to this key in an options.tsx, but with a different static initial value, we will see that value instead:

options.tsx
const [hailingFrequency] = useStorage("hailing", "147")
 
return <p>{hailingFrequency}</p> // "147"

With the above setup, suppose we call setHailingFrequency("8472") in any of the instances above, we will see that all instance will now show "8472" and will now track the value in storage instead of initial value.

Specifying an initial value AND persisting it:

By using a function instead of a static value, the initial value will be persisted into storage memory. The initialize function has one parameter which is the existing value in storage. If there is no value, it is undefined.

Let's say we have a popup.tsx that initialize the state to "42" if there is nothing in storage:

popup.tsx
const [hailingFrequency, setHailingFrequency] = useStorage("hailing", (v) => v === undefined ? "42": v)
...
{hailingFrequency} // "42"

Then, if we make a new hook instance in our content.tsx or options.tsx, we will see the initial value that was persisted, without calling setHailingFrequency:

content.tsx
const [hailingFrequency] = useStorage("hailing")
 
return <p>{hailingFrequency}</p> // "42"

Advanced Hook API usage

When dealing with form input or real-time input, you might need the following:

const [hailingFrequency, {
  setRenderValue,
  setStoreValue,
  remove
}] = useStorage("hailing")
 
return <>
  <input value={hailingFrequency} onChange={(e) => setRenderValue(e.target.value)}/>
  <button onClick={() => setStoreValue()}>
    Save
  </button>
  <button onClick={() => remove()}>
    Remove
  </button>
</>
 
Last updated on December 29, 2022