Performance Mistakes to Avoid

Most slow App Router apps fail for a handful of repeated reasons.

Common App Router performance mistakes
Mistake Hurts Fix
'use client' on a page LCP Move it to the interactive leaf
Heavy library on the client LCP Render it on the server
<img> without dimensions CLS next/image or aspect-ratio
Font swapped in late CLS next/font, self-hosted
Work on every keystroke INP useDeferredValue, useMemo
cookies() in the root layout TTFB Read it where it is needed

The first two are one mistake at different scales. A 'use client' directive high in the tree drags every component below it into the browser bundle, and a library imported inside that boundary ships whether or not it renders. Putting 'use client' on the smallest component that owns state is the most effective habit in an App Router codebase.

The layout shift on /slow measured 0.09 for one image: a hero without dimensions reserves no space, so everything below it jumps when the bytes arrive. next/image writes the aspect ratio into the markup, which is why /fast scores 0. Late fonts shift text the same way.

INP is discovered last, because it needs a real interaction to exist at all. On /slow the filter recomputes 4,000 rows inside the onChange handler and INP reads 184-296 ms across runs; with useDeferredValue and a memoized computation it reads 24-32 ms. The React 7,897 Compiler (The React Compiler) removes some of this work, but cannot make an O(n) loop cheap. Finally, do not fetch in a Client Component's useEffect when a Server Component could fetch during the render: that costs a round trip after hydration, and often shows up as a good FCP with an awful LCP.