react-routerredux-persist

How do I use redux-persist and rtk with React Router v7 Framework (without SSR)?


I'm trying to migrate an existing React/Vite app (currently using React Router v7, along with redux, rtk-query, and redux-persist) to use React Router v7 in Framework mode. I'm hitting an error when I try to run the app using npm run dev:

redux-persist failed to create sync storage. falling back to noop storage.
[vite] (ssr) Error when evaluating SSR module virtual:react-router/server-build: storage.getItem is not a function
...
[vite] Internal server error: storage.getItem is not a function

I can recreate the problem on a fresh app: npx create-react-router@latest

./react-router.config.ts

import type { Config } from "@react-router/dev/config";

export default {
  ssr: false,
} satisfies Config;

./app/store/store.js

import { configureStore } from "@reduxjs/toolkit";
import storage from "redux-persist/lib/storage";
import { persistReducer, persistStore } from "redux-persist";
import auth from "./authSlice";

const persistConfig = {
  key: "root",
  storage,
};

export const reducer = persistReducer(persistConfig, auth);
export const store = configureStore({ reducer });
export const persistor = persistStore(store);

./app/store/authSlice.js

import { createSlice } from "@reduxjs/toolkit";

export const authSlice = createSlice({
  name: "auth",
  initialState: { token: "" },
  reducers: {
    setToken(state, action) {
      state.token = action.payload;
    },
  },
});

export default authSlice.reducer;

And editing root.tsx to add a <Provider />:

import { Provider } from "react-redux";
import { store } from "./store/store";

...

export default function App() {
  return (
    <Provider store={store}>
      <Outlet />
    </Provider>
  );
}

./package.json

{
  ...
  "scripts": {
    ...
    "dev": "react-router dev",
  },
  "dependencies": {
    "@react-router/node": "^7.7.1",
    "@react-router/serve": "^7.7.1",
    "@reduxjs/toolkit": "^2.8.2",
    "isbot": "^5.1.27",
    "react": "^19.1.0",
    "react-dom": "^19.1.0",
    "react-redux": "^9.2.0",
    "react-router": "^7.7.1",
    "redux": "^5.0.1",
    "redux-persist": "^6.0.0"
  },
  "devDependencies": {
    "@react-router/dev": "^7.7.1",
    "@tailwindcss/vite": "^4.1.4",
    "@types/node": "^20",
    "@types/react": "^19.1.2",
    "@types/react-dom": "^19.1.2",
    "tailwindcss": "^4.1.4",
    "typescript": "^5.8.3",
    "vite": "^6.3.3",
    "vite-tsconfig-paths": "^5.1.4"
  }
}

Any ideas how I can get Framework mode up and running without completely replacing redux-persist?


Solution

  • The issue seemed to be with the storage imported from redux-persist - the "noop storage" it falls back to lacks required methods. Following this solution (from a similar Next.js issue) seemed to address the problem. I made these changes to ./app/store/store.js :

    // import storage from "redux-persist/lib/storage";
    import createWebStorage from "redux-persist/lib/storage/createWebStorage";
    
    const createNoopStorage = () => {
      return {
        getItem(_key) {
          return Promise.resolve(null);
        },
        setItem(_key, value) {
          return Promise.resolve(value);
        },
        removeItem(_key) {
          return Promise.resolve();
        },
      };
    };
    
    const storage =
      typeof window === "undefined"
        ? createNoopStorage()
        : createWebStorage("local");
    

    This seems to have solved the issue.