REACT AND NEXT.JS performance optimization guidelines from Vercel Engineering. 70+ rules across 8 categories prioritized by impact. Enforce: no data waterfalls, no moment.js, no barrel imports, Server Components by default, TanStack Query for client fetching. Trigger: writing/reviewing/refactoring React or Next.js code, optimizing bundle size or load times.
REACT AND NEXT.JS performance optimization guidelines from Vercel Engineering. 70+ rules across 8 categories prioritized by impact. Enforce: no data waterfalls, no moment.js, no barrel imports, Server Components by default, TanStack Query for client fetching. Trigger: writing/reviewing/refactoring React or Next.js code, optimizing bundle size or load times.
bundle-barrel-imports, Direct file paths, never barrel index files
bundle-dynamic-imports, next/dynamic for heavy components (charts, editors)
bundle-defer-third-party, Load analytics after hydration
bundle-conditional, Load modules only when feature activated
NEVER:
import { X } from '@/components', barrel imports
Synchronous <script> tags for third-party
import _ from 'lodash', tree-shake to lodash-es
Priority 3: Server-Side Performance
// ✅ Next.js 16, use cache directive for explicit cachingexportdefaultasyncfunctionPage() {
const data = awaitgetCachedData(); // uses React.cache() or 'use cache'
}
// ✅ Parallel fetch in RSCconst [user, posts] = awaitPromise.all([getUser(), getPosts()]);
ALWAYS:
server-hoist-static-io, Fonts, logos at module level
server-parallel-fetching, Promise.all() in RSC
server-cache-react, React.cache() for per-request dedup
server-dedup-props, Avoid duplicate serialization in props
server-auth-actions, Authenticate Server Actions like API routes
NEVER:
Module-level mutable request state in RSC/SSR
Passing entire DB objects to Client Components
Priority 4: Client-Side Data Fetching
// ✅ TanStack Query v5+, caching, dedup, retryconst { data, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
});
// ❌ Raw fetch(), no caching, no dedup, no retryconst [data, setData] = useState();
useEffect(() => { fetch('/api/users').then(setData); }, []);
ALWAYS: TanStack Query v5+ (useQuery, useMutation) for ALL client data
NEVER: Raw fetch() + useEffect for data fetching
NEVER:moment.js or date-fns, use Intl.DateTimeFormat
Priority 5: Re-render Optimization
// ✅ Memoize expensive componentsconstExpensiveChart = memo(functionExpensiveChart({ data }: { data: number[] }) {
// Expensive render work
});
// ✅ Functional setState for stable callbacksconst handleClick = useCallback(() => {
setCount(c => c + 1);
}, []); // No dependency on count
js-index-maps, Build Map for repeated array lookups
js-early-exit, Return early from functions
js-hoist-regexp, Hoist RegExp outside loops
js-combine-iterations, Combine filter/map into single loop
js-tosorted-immutable, Use .toSorted() for immutable sort
js-batch-dom-css, Group CSS changes via classes not individual style sets
Priority 8: Advanced Patterns
advanced-init-once, Initialize app once per load
advanced-use-latest, useLatest for stable callback refs
advanced-effect-event-deps, Don't put useEffectEvent results in deps
VALIDATE: Quality Gates
No data waterfalls: Independent fetches use Promise.all(). Sequential awaits on unrelated data prohibited
No moment.js/date-fns: Date formatting uses native Intl.DateTimeFormat. Zero date library deps
No barrel imports: Imports use direct file paths (e.g., @/components/Button), not @/components
Heavy components use dynamic import: Components with large bundles use next/dynamic + ssr: false
Third-party scripts deferred: Analytics, chat, external scripts use <Script strategy="lazyOnload">, never synchronous
Minimal client serialization: Server Components pass only required primitives to Client Components, never entire DB objects
Data fetching returns error states: Every hook returns isLoading + error. Every container handles both before success
Server deduplication: RSC data fetchers wrapped in React.cache() for per-request dedup
TanStack Query for client data: No raw fetch() in custom hooks, always useQuery/useMutation
No forwardRef: React 19.2, pass ref as normal prop (applicable to new code)
OUTPUT: What This Skill Produces
When applied, the skill categorizes each applicable rule into:
Applied: [rule-name], [what was changed]
Skipped: [rule-name], [why not applicable (e.g., "no third-party scripts in this page")]
Blocked: [rule-name], [what prevents applying it (e.g., "uses library X that requires barrel imports")]
AGENT BEHAVIOR: Mandatory Checks
Check for waterfalls, scan for sequential await on independent data. Combine with Promise.all().
Check date libs, find moment/date-fns imports → replace with Intl.DateTimeFormat
Check imports, find barrel imports → replace with direct file paths
Check heavy components, find large libs (charts, editors) not using dynamic → wrap in next/dynamic