Skip to content

Custom Cache

⚠️

This is still a beta feature. Please install swr@beta to try it out.

By default, SWR uses a global cache to store and share data across all components. Now, there's a new way to customize it with your own cache provider. The new cache configuration and createCache API are now introduced in swr@beta. They're intended to solve problems of using SWR with more customized storages, and providing direct access to the cache.

Create Custom Cache

createCache

This API receive a underlay cache provider as argument. Returns an object, with cache instance that could be consumed by SWR hooks, and mutate API to manipulate the corresponding cache. Note that it's not the global mutate API.

const { mutate, cache } = createCache(provider)

You can pass down cache through SWRConfig or the useSWR hook options.

import { SWRConfig, createCache } from 'swr'
const provider = new Map()
const { mutate, cache } = createCache(provider)
// pass to SWR context
<SWRConfig value={{ cache }}>
<Page />
</SWRConfig>
// or pass to hook options
useSWR(key, fetcher, { cache })
🚨

createCache should not be called inside render, it should be a global singleton.

provider

The provider is used to let user manage cache values directly, and the interface should match the following definition:

interface Cache<Data = any> {
get(key: string): Data | null | undefined
set(key: string, value: Data): void
delete(key: string): void
}

Those methods are being used inside SWR to manage cache. Beyond SWR itself, now user can access the cached keys, values from provider directly. For instance if the provider is a Map instance, you'll be able to access the used keys through provider by using Map.prototype.keys().

🚨

In most cases, you shouldn't directly manipulate cached data. Instead always use mutate to keep the state and cache consistent.

mutate

The usage of the mutate function returned by createCache, is similar to the global mutate function described on the Mutation page, but bound to the specific cache provider. For instance, if you want to revalidate some keys from the given cache:

const { cache, mutate } = createCache(new Map())
export default function App() {
return (
<SWRConfig value={{ cache }}>
<div className="App">
<Section />
<button onClick={() => mutate('A')}>revalidate A</button>
<button onClick={() => mutate('B')}>revalidate B</button>
</div>
</SWRConfig>
)
}

Examples

Mutate Multiple Keys

With the flexibilities of those atomic APIs, you can compose them with your custom logic, such as scheduling partial mutations. In the below example, matchMutate can receive a regex expression as key, and be used to mutate the ones who matched this pattern.

function matchMutate(matcher, data, shouldRevalidate = true) {
const keys = []
if (matcher instanceof RegExp) {
// `provider` is your cache implementation, for example a `Map()`
for (const k of provider.keys()) {
if (matcher.test(k)) {
keys.push(k)
}
}
} else {
keys.push(matcher)
}
const mutations = keys.map((k) => mutate(k, data, shouldRevalidate))
return Promise.all(mutations)
}
matchMutate(/^key-/) // revalidate keys starting with `key-`
matchMutate('key-a') // revalidate `key-a`

Sync Cache to LocalStorage

You might want to sync your cached states to localStorage in some special cases, to recover from the persisted state more easily next time while reloading the app.

function createProvider() {
const map = new Map(JSON.parse(localStorage.getItem('app-cache')) || [])
window.addEventListener('beforeunload', () => {
const appCache = JSON.stringify(Array.from(map.entries()))
localStorage.setItem('app-cache', appCache)
})
return map
}
const provider = createProvider()
const { cache, mutate } = createCache(provider)

Reset Cache Among Tests

let provider
describe('test suite', async () => {
beforeEach(() => {
provider = new Map()
})
it('test case', async () => {
const { cache } = createCache(provider)
useSWR(key, fetcher, { cache })
// ...
})
})