skills/caching-and-data-fetching-patterns/SKILL.md
Caching and data fetching patterns — storage API caching, LRU cache, SWR deduplication, React.cache, preloading, RSC serialization, localStorage versioning, after() for non-blocking work. Use when implementing data fetching, caching, or optimizing network requests.
npx skillsauth add ihj04982/my-cursor-settings caching-and-data-fetching-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Patterns for efficient caching, request deduplication, preloading, and non-blocking side effects.
localStorage, sessionStorage, and document.cookie are synchronous and expensive. Cache reads in memory.
Bad:
function getTheme() {
return localStorage.getItem('theme') ?? 'light'; // called 10 times = 10 reads
}
Good:
const storageCache = new Map<string, string | null>();
function getLocalStorage(key: string) {
if (!storageCache.has(key)) storageCache.set(key, localStorage.getItem(key));
return storageCache.get(key);
}
function setLocalStorage(key: string, value: string) {
localStorage.setItem(key, value);
storageCache.set(key, value);
}
Invalidate on external changes:
window.addEventListener('storage', (e) => { if (e.key) storageCache.delete(e.key); });
React.cache() only works within one request. For data shared across sequential requests, use an LRU cache.
import { LRUCache } from 'lru-cache';
const cache = new LRUCache<string, any>({ max: 1000, ttl: 5 * 60 * 1000 });
export async function getUser(id: string) {
const cached = cache.get(id);
if (cached) return cached;
const user = await db.user.findUnique({ where: { id } });
cache.set(id, user);
return user;
}
With Vercel Fluid Compute, LRU caching is especially effective because concurrent requests share the same function instance.
Reference: node-lru-cache
SWR provides request deduplication, caching, and revalidation across component instances.
Bad (each instance fetches independently):
useEffect(() => { fetch('/api/users').then(r => r.json()).then(setUsers); }, []);
Good (multiple instances share one request):
import useSWR from 'swr';
const { data: users } = useSWR('/api/users', fetcher);
For mutations: useSWRMutation('/api/user', updateUser)
Reference: swr.vercel.app
Use React.cache() for server-side deduplication within a single RSC request.
import { cache } from 'react';
export const getCurrentUser = cache(async () => {
const session = await auth();
if (!session?.user?.id) return null;
return await db.user.findUnique({ where: { id: session.user.id } });
});
Avoid inline objects as arguments — React.cache() uses Object.is (reference equality):
// Bad: always cache miss
getUser({ uid: 1 }); getUser({ uid: 1 });
// Good: cache hit
getUser(1); getUser(1);
In Next.js, fetch is automatically deduplicated. Use React.cache() for DB queries, auth checks, and other non-fetch async work.
Preload heavy bundles on hover/focus to reduce perceived latency.
function EditorButton({ onClick }: { onClick: () => void }) {
const preload = () => { void import('./monaco-editor'); };
return (
<button onMouseEnter={preload} onFocus={preload} onClick={onClick}>
Open Editor
</button>
);
}
RSC serialization deduplicates by object reference, not value. Do transformations (.toSorted(), .filter()) in client, not server.
Bad (duplicates array in RSC payload):
<ClientList usernames={usernames} usernamesOrdered={usernames.toSorted()} />
Good (sends once, transforms in client):
<ClientList usernames={usernames} />
// Client: const sorted = useMemo(() => [...usernames].sort(), [usernames]);
Operations that break deduplication: .toSorted(), .filter(), .map(), .slice(), {...obj}, structuredClone().
Add version prefix to keys and store only needed fields.
const VERSION = 'v2';
function saveConfig(config: { theme: string; language: string }) {
try {
localStorage.setItem(`userConfig:${VERSION}`, JSON.stringify(config));
} catch {} // Throws in incognito, quota exceeded
}
Always wrap in try-catch. Never store tokens/PII.
Use Next.js after() to schedule work after response is sent.
Bad (logging blocks response):
await updateDatabase(request);
await logUserAction({ userAgent }); // blocks
return Response.json({ status: 'success' });
Good:
import { after } from 'next/server';
await updateDatabase(request);
after(async () => { logUserAction({ sessionCookie, userAgent }); });
return Response.json({ status: 'success' });
Common use cases: analytics, audit logging, notifications, cache invalidation.
Reference: next/server after()
React.cache()after() to avoid blocking responsedevelopment
Conduct WCAG 2.2 accessibility audits with automated testing, manual verification, and remediation guidance. Use when auditing websites for accessibility, fixing WCAG violations, or implementing accessible design patterns.
research
Generate high-entropy research (자료조사) and ideas (아이디어) using Verbalized Sampling to avoid mode collapse and maximize creativity and novelty.
development
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
documentation
Sync documentation from source-of-truth (package.json, .env.example). Generates CONTRIB.md, RUNBOOK.md. Use when updating project docs or after adding scripts/env vars.