> Markdown version of [Client-Side Data Caching](https://vaadin.com/docs/latest/hilla/guides/client-caching). Section index: [llms.txt](https://vaadin.com/docs/latest/hilla/llms.txt)

# Client-Side Data Caching

Hilla caches client-side views out of the box, but not client-side data. This can even lead to a situation where the client-side view is available offline, which isn’t very useful, since there is no data. To overcome this, you can use [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to store the client-side data, so that the data is available even offline.

## <a id="cacheable-wrapper-with-local-storage"></a>Cacheable Wrapper with Local Storage

Assume you have a function to retrieve the data from the backend. After the data is retrieved, you can save it into `localStorage` as a key-value pair. It’s also good to provide a default value, in case the data is available from neither the backend nor cache.

**Example**: define a cacheable helper in `cacheable.ts` to cache client-side data.

```typescript
export async function cacheable<T>(fn: () => Promise<T>, key: string, defaultValue: T) {
  let result;
  try {
    // retrieve the data from backend.
    result = await fn();
    // save the data to localStorage.
    localStorage.setItem(key, JSON.stringify(result));
  } catch {
    // if failed to retrieve the data from backend, try localStorage.
    const cached = localStorage.getItem(key);
    // use the cached data if available, otherwise the default value.
    result = cached ? JSON.parse(cached) : defaultValue;
  }

  return result;
}
```

Now you can use the cacheable wrapper in your client-side view.

**Example**: use the cacheable wrapper.

```typescript
// import the cacheable function from the path to cacheable.ts file (without the suffix)
import { cacheable } from 'path-to-cacheable';

// instead of always relying on the backend to retrieve the data. e.g.
// const stats = await getStats();
// you can now wrap getStats into the cacheable helper.
const stats = await cacheable(() => getStats(), 'stats', {});
```

## <a id="clear-the-local-storage"></a>Clear the Local Storage

It’s good practice to clear the data stored in the `localStorage`, for example when a user logs out. Suppose there is a `logout` event. The above cacheable wrapper could be improved as:

```typescript
// the name of the cache for offline usage
const CACHE_NAME = 'offline-cache';

window.addEventListener('logout', () => {
  // clear the data from localStorage when a user logs out
  clearCache();
});

export async function cacheable<T>(fn: () => Promise<T>, key: string, defaultValue: T) {
  let result;
  try {
    // retrieve the data from backend.
    result = await fn();
    // save the data to localStorage.
    const cache = getCache();
    cache[key] = result;
    localStorage.setItem(CACHE_NAME, JSON.stringify(cache));
  } catch {
    // if failed to retrieve the data from backend, try localStorage.
    const cache = getCache();
    const cached = cache[key];
    // use the cached data if available, otherwise the default value.
    result = cached === undefined ? defaultValue : cached;
  }

  return result;
}

function getCache(): any {
  const cache = localStorage.getItem(CACHE_NAME) || '{}';
  return JSON.parse(cache);
}

function clearCache() {
  localStorage.removeItem(CACHE_NAME);
}
```
