---
title: "Frontend — localStorage Key Migration When Rebranding"
aliases: [localstorage-key-migration, storage-key-rename, rebrand-session-loss, zustand-persist-rename]
tags: [frontend, localstorage, zustand, react, rebrand, auth, migration]
sources:
  - "daily/2026-04-28.md"
created: 2026-04-28
updated: 2026-04-28
---

# Frontend — localStorage Key Migration When Rebranding

When a frontend app is rebranded and its localStorage key names are changed (e.g., from `barclays-auth-storage` to `oliver-auth-storage`), all existing user sessions are silently invalidated. The old keys remain in the browser's localStorage but are no longer read — on next load, the app finds no data under the new key name and treats the user as unauthenticated.

## Key Points

- **Old localStorage keys persist forever** — renaming the key in code does not migrate existing browser data; the old key sits orphaned, the new key is empty
- **All users are effectively logged out** after a key rename deploy — no error, no warning; the app simply starts fresh as if the user never logged in
- **Zustand `persist` middleware uses a `name` option as the localStorage key** — changing this name causes the same session loss
- **Fix: migration script at app entry point** — on load, check for old key, copy data to new key, delete old key; runs once per user, transparent
- **Decision to keep internal keys**: in barclays-banner-builder, internal session keys (`barclays-*`) were intentionally NOT renamed to avoid invalidating in-flight sessions during the rebrand; only UI-visible branding strings were changed

## Details

### Why This Happens

Browsers store localStorage data keyed by the exact string used in `localStorage.setItem(key, value)`. When a Zustand `persist` store has `name: "barclays-auth-storage"`, Zustand writes to and reads from that exact string. When the name is changed to `"oliver-auth-storage"`, Zustand looks for the new key — finds nothing — initialises with the default (unauthenticated) state. The old `barclays-auth-storage` entry remains in the browser forever.

This is not a bug in Zustand or localStorage — it's the expected behavior of key-value storage. The same problem occurs with any keyed storage: sessionStorage, IndexedDB, cookie names, etc.

### Symptom

After a rebrand deploy:
- Users who were logged in are suddenly redirected to the login page
- No error in the console or network tab
- `Application → Local Storage` in DevTools shows both old and new keys: old with data, new empty
- Logging in again works fine (new key is populated); old key remains as clutter

### The Migration Script

Add to `main.tsx` (or the app's entry point), before the React tree mounts:

```typescript
// main.tsx — runs once before React mounts
function migrateStorage() {
  const OLD_KEY = "barclays-auth-storage";
  const NEW_KEY = "oliver-auth-storage";

  const oldData = localStorage.getItem(OLD_KEY);
  if (oldData && !localStorage.getItem(NEW_KEY)) {
    // Copy old data to new key
    localStorage.setItem(NEW_KEY, oldData);
    // Clean up old key
    localStorage.removeItem(OLD_KEY);
    console.log("[migration] Migrated localStorage from", OLD_KEY, "to", NEW_KEY);
  }
}

migrateStorage();

ReactDOM.createRoot(document.getElementById("root")!).render(<App />);
```

This migration is safe to ship before or alongside the key rename: if old data exists and new key is empty, migrate; otherwise no-op.

### When to Keep Old Keys (No Migration)

In some cases, deliberately not migrating is the right choice:

- **During a deploy with in-flight user sessions**: renaming now would log everyone out immediately; decide whether a one-time logout is acceptable
- **When the stored data format changed** during the rebrand: migrating old-format data into a new-format key would corrupt state; better to let users re-login with a clean slate
- **Internal-only keys** (e.g., cache keys, feature flags): user impact is minimal, no migration needed

barclays-banner-builder 2026-04-28 decision: `barclays-*` session keys kept unchanged because the rebrand was cosmetic (visual redesign only), and invalidating active work sessions mid-project was unacceptable.

### Zustand-Specific Pattern

For Zustand `persist` stores, the migration can also be done inside the store's `onRehydrateStorage` callback:

```typescript
// Alternative: migrate inside Zustand store initialisation
export const useAuthStore = create<AuthState>()(
  persist(
    (set) => ({ /* ... */ }),
    {
      name: "oliver-auth-storage",   // ← new key
      onRehydrateStorage: () => {
        // Check for old key and migrate if needed
        const old = localStorage.getItem("barclays-auth-storage");
        if (old) {
          localStorage.setItem("oliver-auth-storage", old);
          localStorage.removeItem("barclays-auth-storage");
        }
      },
    }
  )
);
```

This is neater for Zustand stores but couples the migration to the store initialisation rather than the app entry point.

### Other Storage Types

The same pattern applies to:
- `sessionStorage` — same API, same key-rename problem (though sessions are cleared on tab close anyway)
- Cookie names — renaming a session cookie name logs all users out; same migration approach applies server-side
- IndexedDB database names — renaming requires schema migration via `onupgradeneeded`

## Related Concepts

- [[wiki/concepts/zustand-async-hydration]] — Zustand persist hydration timing; keys are the configuration point where naming matters
- [[wiki/concepts/memory-compiler-mac-migration]] — similar "config key mismatch" pattern: hooks reference paths/identifiers that break when the environment changes
- [[wiki/client-knowledge/barclays]] — barclays-banner-builder rebrand context where this was encountered

## Sources

- [[daily/2026-04-28.md]] — barclays-banner-builder UI rebrand from Barclays design tokens to Oliver Modcomms design system; `barclays-*` localStorage keys intentionally kept to avoid session invalidation; decision documented as tech debt item (migrate to `oliver-*` in a future release with migration script)
