Every project starts with a performance budget: LCP under 2.0s on mid-range mobile, total JS under 200 kB on landing routes. CI fails builds that breach it — which changes conversations from "we should optimize later" to "this component needs to earn its bytes."
The usual wins are unglamorous: next/image with explicit sizes, fonts self-hosted with swap, third-party scripts loaded after interaction, and heavy libraries (charts, editors) split behind dynamic imports. Animations run on transform and opacity only.
The last few points come from HTML: server components by default, streaming for slow data, and honest empty states so nothing shifts as content lands.