Quickstarts
/
Quickstart: Redux

Quickstart with Redux

Easily integrate Redux to provide a simple way to manage state across your browser extension.

We maintain a custom fork of redux-persist which you can utilize along with redux-persist-webextension-storage to persist your Redux state using chrome.storage.

Just a heads up, the current version does not support content scripts. If you'd like content script support, please file an issue, and we will prioritize adding that functionality.

Increment Example

Let's make a basic extension that increments and decrements a counter.

Create Your Slice

counter-slice.ts

import { createSlice } from "@reduxjs/toolkit"

export interface CounterState {
  count: number
}

const counterSlice = createSlice({
  name: "counter",
  initialState: { count: 0 },
  reducers: {
    increment: (state) => {
      state.count += 1
    },
    decrement: (state) => {
      state.count -= 1
    }
  }
})

export const { increment, decrement } = counterSlice.actions

export default counterSlice.reducer

Create Your Store

store.ts

import {
  FLUSH,
  PAUSE,
  PERSIST,
  PURGE,
  REGISTER,
  REHYDRATE,
  RESYNC,
  persistReducer,
  persistStore
} from "@plasmohq/redux-persist"
import { Storage } from "@plasmohq/storage"
import { configureStore } from "@reduxjs/toolkit"
import { localStorage } from "redux-persist-webextension-storage"

import counterSlice from "~counter-slice"

const rootReducer = counterSlice

const persistConfig = {
  key: "root",
  version: 1,
  storage: localStorage
}

const persistedReducer = persistReducer(persistConfig, rootReducer)

export const store = configureStore({
  reducer: persistedReducer,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: [
          FLUSH,
          REHYDRATE,
          PAUSE,
          PERSIST,
          PURGE,
          REGISTER,
          RESYNC
        ]
      }
    })
})
export const persistor = persistStore(store)

// This is what makes Redux sync properly with multiple pages
// Open your extension's options page and popup to see it in action
new Storage().watch({
  [`persist:${persistConfig.key}`]: () => {
    persistor.resync()
  }
})

The thing to note is the new Storage().watch() call. This will automatically resync the store whenever the Redux store changes in chrome.storage.

Using in React

options.tsx

import { PersistGate } from "@plasmohq/redux-persist/integration/react"
import { Provider } from "react-redux"

import { CounterView } from "~counter"
import { persistor, store } from "~store"

function Options() {
  return (
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <CounterView />
      </PersistGate>
    </Provider>
  )
}

export default Options

Using the PersistGate will ensure the child components don't render until the store is ready.

Using in a Background Service Worker

import { persistor, store } from "~store"

persistor.subscribe(() => {
  console.log("State changed with: ", store?.getState())
})

Full Example

See with-redux for a complete example.