Contents
After migrating the whole site to the App Router, the error I hit most was not a type error — it was hydration mismatch.
Never read browser state during render
Anything undecidable on the server — localStorage, matchMedia, new Date() — must stay out of the first paint.
The pattern I settled on: a stable server default, then an effect that swaps in the real value after mount.
Push the client boundary down
One 'use client' pulls the whole subtree into the client bundle, so it belongs as close as possible to the element that actually needs interactivity.
The higher you draw the boundary, the more it costs.
