Vercel React Best Practices — Repo Guidance
Source: .agents/references/coding-standard/vercel-react-best-practices/REPO-GUIDANCE.md
Content
Vercel React Best Practices — Repo Guidance
Performance optimization guidance for React and Next.js in this repository, adapted from Vercel Engineering best practices (MIT). The pack contains 70 rules across 8 categories, prioritized by impact.
Entry point: read this file first, then open detailed rule files under rules/ as needed.
Default application
Load this guidance together with .agents/rules/*.mdc whenever agent work touches:
src/**/*.tsxor React-relatedsrc/**/*.ts- Next.js Pages Router (
src/pages/),getStaticProps/getServerSideProps, andpages/api - Hooks, client/server data fetching, or service callers from UI
- Imports, dynamic imports, third-party libraries, or bundle-size changes
- Rendering, re-render, hydration, or JavaScript hot-path review
The always-applied rule .agents/rules/vercel-react-performance.mdc points here. Non-frontend tasks (docs-only, infra-only, etc.) do not need a deep read of this pack.
Repo precedence
If Vercel guidance conflicts with repo rules, repo rules win. In particular:
- Follow
.agents/rules/clean-react.mdcfor unnecessaryuseEffect, unnecessary state, and memoization discipline. - Follow
.agents/rules/no-unnecessary-usecallback.mdc; do not adduseCallbackonly because a generic performance checklist mentions stable callbacks. - Follow
.agents/rules/no-reexport-index-files.mdc; avoid barrel files in app code. - Use this repo's existing data-fetching and service patterns (
{ data, error }, TanStack Query, etc.) before introducing new libraries.
Pages Router (this repo)
This codebase uses Next.js Pages Router only (src/pages/, index.page.tsx). There is no src/app/, no Server Actions ("use server"), and no React Server Components in application code.
Do not introduce App Router routes, Server Actions, or RSC patterns unless the user explicitly requests a migration.
Data-fetching defaults:
- Page data:
getStaticProps/getServerSideProps(often viagetDefaultStaticPropsinsrc/utility/getDefaultProps.js) - Client data: TanStack Query and
@/services/with{ data, error }(see.agents/rules/clean-react.mdc,no-try-catch-service-api.mdc) - Mutations and utilities:
src/pages/api/handlers (NextApiRequest/NextApiResponse)
Interpret server- rules through Pages Router (props serialized into the page payload), not RSC trees.
Rule applicability
| Status | Rules |
|---|---|
| Apply as-is | async-cheap-condition-before-await, async-defer-await, async-parallel, async-dependencies, async-api-routes (Pages), all bundle-*, client-*, rerender-*, rendering-*, js-*, advanced-* |
| Adapt (Pages) | server-serialization, server-dedup-props, server-parallel-fetching, server-parallel-nested-fetching, server-no-shared-module-state, server-hoist-static-io, server-cache-lru, server-cache-react (limited), async-suspense-boundaries (client Suspense only) |
| App Router only (skip here) | server-auth-actions when documented as "use server" (use API-route auth instead), server-after-nonblocking (after()), RSC-style async Server Component streaming in async-suspense-boundaries |
How to use this pack
- Skim the category table and quick reference below for the concern you are implementing or reviewing.
- Open the matching file:
rules/<rule-id>.md(e.g.rules/async-parallel.md). - Each rule file includes why it matters, incorrect/correct examples, and extra context.
Rule categories by priority
| Priority | Category | Impact | Prefix |
|---|---|---|---|
| 1 | Eliminating Waterfalls | CRITICAL | async- |
| 2 | Bundle Size Optimization | CRITICAL | bundle- |
| 3 | Server-Side Performance | HIGH | server- |
| 4 | Client-Side Data Fetching | MEDIUM-HIGH | client- |
| 5 | Re-render Optimization | MEDIUM | rerender- |
| 6 | Rendering Performance | MEDIUM | rendering- |
| 7 | JavaScript Performance | LOW-MEDIUM | js- |
| 8 | Advanced Patterns | LOW | advanced- |
Quick reference
1. Eliminating Waterfalls (CRITICAL)
async-cheap-condition-before-await- Check cheap sync conditions before awaiting flags or remote valuesasync-defer-await- Move await into branches where actually usedasync-parallel- Use Promise.all() for independent operationsasync-dependencies- Use better-all for partial dependenciesasync-api-routes(Pages) - Start promises early, await late inpages/apihandlersasync-suspense-boundaries(Pages: client Suspense; App-only: async RSC streaming) - Defer slow UI with client<Suspense>,next/dynamic, or client queries
2. Bundle Size Optimization (CRITICAL)
bundle-barrel-imports- Import directly, avoid barrel filesbundle-analyzable-paths- Prefer statically analyzable import and file-system paths to avoid broad bundles and tracesbundle-dynamic-imports- Use next/dynamic for heavy componentsbundle-defer-third-party- Load analytics/logging after hydrationbundle-conditional- Load modules only when feature is activatedbundle-preload- Preload on hover/focus for perceived speed
3. Server-Side Performance (HIGH)
server-auth-actions(Pages: API routes; App-only:"use server") - Authenticatepages/apihandlers; Server Actions N/A in this reposerver-cache-react(Pages: limited) - PreferPromise.allin data functions;React.cache()is App Router/RSCserver-cache-lru(Pages) - LRU cache for cross-request cachingserver-dedup-props(Pages) - Avoid duplicate fields ingetStaticProps/getServerSidePropsreturn valuesserver-hoist-static-io(Pages) - Hoist static I/O (fonts, logos) to module levelserver-no-shared-module-state(Pages) - No mutable module scope for per-request data during GSSP/SSRserver-serialization(Pages) - Minimize props returned from page data functionsserver-parallel-fetching(Pages) -Promise.allingetStaticProps/getServerSidePropsserver-parallel-nested-fetching(Pages) - Per-item promise chains insidePromise.allserver-after-nonblocking(App-only) -after(); not used in Pages Router here
4. Client-Side Data Fetching (MEDIUM-HIGH)
client-swr-dedup- Use SWR for automatic request deduplicationclient-event-listeners- Deduplicate global event listenersclient-passive-event-listeners- Use passive listeners for scrollclient-localstorage-schema- Version and minimize localStorage data
5. Re-render Optimization (MEDIUM)
rerender-defer-reads- Don't subscribe to state only used in callbacksrerender-memo- Extract expensive work into memoized componentsrerender-memo-with-default-value- Hoist default non-primitive propsrerender-dependencies- Use primitive dependencies in effectsrerender-derived-state- Subscribe to derived booleans, not raw valuesrerender-derived-state-no-effect- Derive state during render, not effectsrerender-functional-setstate- Use functional setState for stable callbacksrerender-lazy-state-init- Pass function to useState for expensive valuesrerender-simple-expression-in-memo- Avoid memo for simple primitivesrerender-split-combined-hooks- Split hooks with independent dependenciesrerender-move-effect-to-event- Put interaction logic in event handlersrerender-transitions- Use startTransition for non-urgent updatesrerender-use-deferred-value- Defer expensive renders to keep input responsivererender-use-ref-transient-values- Use refs for transient frequent valuesrerender-no-inline-components- Don't define components inside components
6. Rendering Performance (MEDIUM)
rendering-animate-svg-wrapper- Animate div wrapper, not SVG elementrendering-content-visibility- Use content-visibility for long listsrendering-hoist-jsx- Extract static JSX outside componentsrendering-svg-precision- Reduce SVG coordinate precisionrendering-hydration-no-flicker- Use inline script for client-only datarendering-hydration-suppress-warning- Suppress expected mismatchesrendering-activity- Use Activity component for show/hiderendering-conditional-render- Use ternary, not && for conditionalsrendering-usetransition-loading- Prefer useTransition for loading staterendering-resource-hints- Use React DOM resource hints for preloadingrendering-script-defer-async- Use defer or async on script tags
7. JavaScript Performance (LOW-MEDIUM)
js-batch-dom-css- Group CSS changes via classes or cssTextjs-index-maps- Build Map for repeated lookupsjs-cache-property-access- Cache object properties in loopsjs-cache-function-results- Cache function results in module-level Mapjs-cache-storage- Cache localStorage/sessionStorage readsjs-combine-iterations- Combine multiple filter/map into one loopjs-length-check-first- Check array length before expensive comparisonjs-early-exit- Return early from functionsjs-hoist-regexp- Hoist RegExp creation outside loopsjs-min-max-loop- Use loop for min/max instead of sortjs-set-map-lookups- Use Set/Map for O(1) lookupsjs-tosorted-immutable- Use toSorted() for immutabilityjs-flatmap-filter- Use flatMap to map and filter in one passjs-request-idle-callback- Defer non-critical work to browser idle time
8. Advanced Patterns (LOW)
advanced-effect-event-deps- Don't putuseEffectEventresults in effect depsadvanced-event-handler-refs- Store event handlers in refsadvanced-init-once- Initialize app once per app loadadvanced-use-latest- useLatest for stable callback refs
Attribution
Upstream: Vercel Engineering React/Next.js performance best practices. License: MIT.