A React Performance Checklist Before You Ship
April 18, 2026
A practical pre-launch checklist for React and Next.js apps — render performance, bundle size, and the Core Web Vitals fixes that actually move the needle.
Most React performance problems aren't exotic — they're the same handful of issues showing up in a new component tree. Before shipping a build, this is the checklist I actually run through, roughly in the order it tends to pay off.
Audit re-renders before optimizing anything else
Before reaching for `useMemo` or `useCallback` anywhere, profile first — React DevTools' Profiler tab shows exactly which components re-render and why. A shockingly common finding is a single high-level state update re-rendering an entire tree because state that only three components need lives at the app root. Moving state down (or splitting a context into smaller, more targeted ones) usually fixes more than any amount of manual memoization.
Code-split anything not needed on first paint
Route-based code splitting is table stakes with Next.js, but it's worth going further: modals, settings panels, and anything behind a click or a scroll shouldn't be in the initial bundle. `next/dynamic` with a loading fallback keeps the first paint lean without changing how the component is used elsewhere in the code.
Get serious about images
Unoptimized images are still the most common cause of a bad Largest Contentful Paint score. `next/image` handles resizing and format negotiation automatically, but it only helps if `width`/`height` (or `fill` with a sized parent) are set correctly — a missing dimension is what causes layout shift even when the image itself loads fast.
Check what you're actually shipping
Running a bundle analyzer against the production build regularly turns up surprises: a date library imported for one formatting call that pulls in the entire locale set, or a component library imported wholesale when only three components from it are used. Tree-shaking helps, but only if imports are written in a way that allows it (named imports from packages that support ESM, not blanket namespace imports).
Don't skip the un-glamorous stuff
Debouncing search inputs, virtualizing long lists instead of rendering every row, and setting explicit `key` props on list items (not array index, if the list can reorder) rarely make it into a highlight reel, but they're responsible for most of the perceived-speed difference between an app that feels instant and one that feels sluggish under real data volumes.
Measure against real devices, not just your laptop
Core Web Vitals numbers from a fast dev machine on fast wifi are close to meaningless for a user on a mid-range phone on patchy mobile data. Throttling CPU and network in DevTools before calling something 'fast enough' catches a surprising number of issues that a fast local build hides completely.