Back to list
tech-with-seth

add-caching

by tech-with-seth

React Router 7 starter with Polar.sh, BetterAuth, Prisma, and Tailwind

1🍴 0📅 Jan 25, 2026

SKILL.md


name: add-caching description: Add client-side caching with stale-while-revalidate strategy. Use when optimizing page load performance or reducing server requests.

Add Caching

Adds client-side caching using remix-client-cache with a stale-while-revalidate (SWR) strategy.

When to Use

  • Improving page load performance
  • Reducing server requests for frequently accessed data
  • Caching loader data across navigations
  • User asks to "add caching", "improve performance", or "cache data"

How SWR Works

  1. Returns cached (stale) data immediately
  2. Refreshes data in the background
  3. Hot-swaps updated data when available

Step 1: Configure Global Storage

Location: app/entry.client.tsx

import { configureGlobalCache } from "remix-client-cache";

// Use localStorage for persistent cache
configureGlobalCache(() => localStorage);

// Or sessionStorage (cleared when tab closes)
configureGlobalCache(() => sessionStorage);

Step 2: Create Cached Client Loader

import { createClientLoaderCache } from "remix-client-cache";
import type { Route } from "./+types/my-route";

export async function loader({ request }: Route.LoaderArgs) {
    const data = await fetchData();
    return { data };
}

// Generate cached client loader
export const clientLoader = createClientLoaderCache({
    loader,
    // Optional: custom cache key
    key: (args) => `products-${args.params.id}`,
});

// Enable client loader hydration
clientLoader.hydrate = true;

Step 3: Use Cached Data in Component

Option A: useCachedLoaderData Hook

import { useCachedLoaderData } from "remix-client-cache";
import type { Route } from "./+types/my-route";

export default function MyRoute() {
    const data = useCachedLoaderData<typeof loader>();

    return (
        <div>
            <h1>{data.title}</h1>
        </div>
    );
}

Option B: CacheRoute Wrapper

import { CacheRoute } from "remix-client-cache";
import type { Route } from "./+types/my-route";

export default function MyRoute() {
    return (
        <CacheRoute>
            {(data) => (
                <div>
                    <h1>{data.title}</h1>
                </div>
            )}
        </CacheRoute>
    );
}

Cache Invalidation

After Mutations

import { decacheClientLoader } from "remix-client-cache";
import type { Route } from "./+types/my-route";

export async function clientAction({ request }: Route.ClientActionArgs) {
    const result = await submitForm(request);

    // Clear the cache for this route
    await decacheClientLoader();

    return result;
}

Manual Invalidation

import { invalidateCache, useCacheInvalidator } from "remix-client-cache";

// Direct invalidation
await invalidateCache("my-cache-key");
await invalidateCache(["key1", "key2"]);

// Hook-based invalidation
function RefreshButton() {
    const invalidate = useCacheInvalidator();

    return (
        <button onClick={() => invalidate("my-cache-key")}>
            Refresh
        </button>
    );
}

Manual Cache Wrapper

For more control:

import { cacheClientLoader } from "remix-client-cache";
import type { Route } from "./+types/my-route";

export async function clientLoader(args: Route.ClientLoaderArgs) {
    return cacheClientLoader(args, {
        key: `user-${args.params.id}`,
        adapter: () => sessionStorage,
    });
}

clientLoader.hydrate = true;

Storage Options

StoragePersistenceUse Case
localStoragePersists across sessionsUser preferences, stable data
sessionStorageCleared when tab closesSensitive or session-specific data
In-memoryCleared on reloadDevelopment, testing

Best Practices

  1. Configure storage early - Set up in entry.client.tsx before hydration
  2. Use meaningful cache keys - Include dynamic parameters: products-${productId}
  3. Invalidate after mutations - Always clear cache in clientAction
  4. Set hydrate = true - Enable proper hydration for cached data
  5. Consider data freshness - Not all data should be cached

When NOT to Cache

  • Real-time data that changes frequently
  • User-specific sensitive data (with localStorage)
  • Data that must always be fresh
  • Small, fast API responses

Anti-Patterns

  • Forgetting to invalidate cache after mutations
  • Using localStorage for sensitive data
  • Caching without considering data freshness
  • Not setting hydrate = true on clientLoader

Full Reference

See .github/instructions/client-side-caching.instructions.md for comprehensive documentation.

Score

Total Score

65/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon