Run these checks before you ship. Each item is a verification, not a principle. The reason behind each item is in Section Performance Rules.
Checklist A: A new theme
Do these one time, when you build the shared parts of the theme.
-
A shared
useIsMobile()hook exists, and it is the only device detection in the theme. The hook reads the device class from the request User-Agent on the server. No component readswindow.innerWidthormatchMediaat render time. -
A shared media wrapper exists that reserves space. Every image, video, and advertisement component uses it. No bare
<img>with an automatic height. -
A shared skeleton component exists for content that the client fetches, so that each section does not build its own.
-
The font strategy is set up. The display font uses
font-display: optional, thewoff2file is preloaded, and a metric-matched fallback@font-faceis in the base stylesheet. -
Build hygiene is correct. Shared libraries are imported deep, not from the barrel. Analytics libraries load as lazy chunks. Large decorative SVG files are asset URLs, not inline components.
-
You can measure. You can run Lighthouse on mobile with a throttle, on the plain URL, and read TBT and CLS.
-
If the theme uses deferred hydration:
deferredHydrationis declared on the theme bundle root with anssrSectionCountof about 6 or more, adefaultMinHeight, and asectionMinHeightsmap. Start the map empty and add one entry for each section as you build it. -
If the theme uses route prefetch:
routePrefetch.routeslists the three to fivepageTypeidentifiers a shopper most often opens next.
Checklist B: A new section
Do these for every section.
-
Every image, video, and advertisement slot sits in a box that reserves its height. The ratio comes from server-stable data only. Content images use the intrinsic dimensions first. Advertisement slots use the configured ratio only.
-
The first render has no gate on
window,mounted,matchMedia,Date.now(), orisRunningOnClient(). The server HTML equals the first client render. Every browser-only read happens in an effect, a handler, or an idle callback. -
Device branching uses the shared
useIsMobile()hook or a CSS media query. -
A section that fetches content in the browser renders its skeleton on the first render, not
null, at the same height as the real content. -
The content height is predictable. Images are sized, text is clamped to a fixed number of lines, and meta rows have a fixed height, so that the skeleton matches the content. Content of unknown length is bounded.
-
Every upgrade after hydration is scheduled at
requestIdleCallback. It resolves, then sets the state, and it cancels on unmount. -
A heavy library loads at idle, behind static markup that matches what the library creates. The section skips the library when it does not need it.
-
There is no animation driven by a JavaScript timer. All motion is CSS.
-
Imports are deep, not barrel. Analytics and large static SVG files are outside the synchronous bundle.
-
If the section cannot render on the server, its source file contains
export const ssr = false;. The marker is in the source, not in the generated registry, and the section has no secondmountedgate on top of it. -
The reserved height matches the section behaviour. A section that renders content has a
sectionMinHeightsentry equal to its real height. A section that can render nothing has no entry. -
Layout shift is zero, and it is zero on every run. Measure on a throttled network, on the plain URL. The shift must not depend on when an API answers. If the section is deferred, scroll it into view and confirm that the swap from placeholder to content does not move the page.
The two-minute audit
If you have time for four checks only, do these. They find the most frequent and the most expensive problems.
-
Search the render path of the section for
window.,innerWidth,matchMedia,isRunningOnClient,Date.now, andnew Date. A result at render time is a hydration-mismatch risk. See Rule 1. -
Confirm that every
<img>has a reserved box. No media has an automatic height. See Rule 2. -
Confirm that every section which fetches content in the browser renders a matched skeleton at the same height as its content. See Rule 3.
-
Search the section code for
setInterval. A timer that sets state costs main-thread time on every tick. See Rule 5.