- Shipped
- August 20, 2026 at 3:14 AM UTC
- Author
- Kamo
- Commit
- 721669f
Three changes, all removing work from the window where the page is being judged. `(pages)/layout.tsx` carried `"use client"` and never needed it — no state, no effect, no handler, three elements composed together. The directive was load-bearing in the wrong direction: it made the layout a client boundary, so the whole subtree re-rendered on every navigation, and it made a server-side footer impossible, because a client component cannot import a server one. That unblocked the footer, which was `"use client"` for exactly two buttons — "cookie settings" and "back to top". The directive is not selective, so ~29 links, four column headings, the social row and five paragraphs of legal fine print all shipped as JavaScript and hydrated. The footer is in the shared layout, so that was every page's cost, not one page's. The buttons are now a small island and the per-link analytics became `data-track` attributes, which the delegated listener already in TrackedClicks reads — so a server-rendered link still reports its click without carrying a handler. TrackedClicks moves to the root layout, since the footer now depends on it everywhere rather than on the homepage only. Honest accounting on that one: it is close to neutral on bytes. A client component costs a module reference in the flight payload; a server component costs its whole rendered tree, so the payload grows 216 KB -> 239 KB raw (+3.5 KB gzipped) while eager JS drops only slightly. What it actually buys is the hydration pass over the footer's ~150 elements, on every page, and a shared layout that no longer re-renders on navigation. ScrollProgress and JumpToBar are now idle-mounted. Neither can be seen on arrival — the progress bar sits at zero width and JumpToBar hides itself until `scrollY > 300` — so both were hydrating and attaching scroll listeners to render nothing at the moment that matters. They are also the two components here where deferring is provably free of layout cost: both are `position: fixed`, so they cannot shift anything when they arrive. Worth checking rather than assuming, with CLS at a quarter of the mobile score. `experimental.inlineCss` was re-tested and stays off, now for a measured reason rather than a remembered one. The theory was that its earlier bad result came from framer-motion and gsap saturating the main thread; both are gone, so it was worth retrying. It is not the libraries. The flag takes the document from 53 KB to 93.4 KB gzip while the stylesheet is 17.8 KB gzip on its own, because Next inlines it as a <style> block AND escapes a second copy into the RSC flight payload: <style> 96.7 KB, inline script 216 KB -> 334 KB. That +118 KB is script the main thread must parse before anything else — the exact shape of the TBT spike that cost 20 points the first time. Render-blocking CSS is Unscored; TBT is 30% of the mobile score.