Tech

How we hit Core Web Vitals on every same-day launch

8 min readSame Day Website Launch

A practical playbook: image budgets, font subsetting, JS gating, and why we ship 0kb of analytics in the first 100 KB.

Google's Core Web Vitals — LCP, INP, CLS — are now an explicit ranking signal in mobile search, and as of the March 2026 algorithm update the three are weighted equally. Most agency builds fail at least one of the three. Here's the playbook we use to pass all three on every same-day launch.

Why this matters more in 2026 than it did in 2024

When Google replaced FID with INP in March 2024, most agencies treated it as an internal change with no real teeth. The March 2026 update closed that loophole — a poor INP score now carries the same ranking penalty as a poor LCP score, and 43% of measured sites currently fail the 200 ms INP threshold. Sites in the "needs improvement" band saw measurable position drops averaging 0.8 places in the rolling six-month tracking studies. For a site sitting at position 6 for its money keyword, 0.8 places is the difference between 4.7% CTR and 3.2% CTR — a quarter of your click volume gone.

The thresholds, current to 2026

Pass thresholds: LCP under 2.5 seconds, INP under 200 milliseconds, CLS under 0.1. Field data (CrUX), 75th-percentile, mobile bucket — that is the only measurement Google uses for ranking. Lab tools (Lighthouse, PageSpeed Insights' simulated run) are useful diagnostics, but they will not save you if your real users on Android Mid-tier devices over slow 4G consistently get a 3.4-second LCP. We tune to the field data because that is what gets scored.

Image budget: 200 KB above the fold

Hero images go through next/image with AVIF + WebP fallbacks. We ban anything heavier than 200 KB above the fold. A typical hero ships at 38–60 KB.

How we hit those numbers

Three steps: (1) source images at the exact width they render at on mobile — a 1920 × 1080 hero on a 375 px viewport is wasting 5× the bytes; (2) generate AVIF first, WebP as fallback for older iOS, JPEG/PNG as last resort — next/image handles the negotiation automatically; (3) for photographs, accept that a small amount of compression artefact is fine at quality 70–75; for product shots we go to 80–85. Logos and screenshots go through SVGO. Background "texture" images get baked into a CSS gradient where the design tolerates it — zero KB instead of forty.

Font subsetting

Two display fonts max. Both subset to the latin range, both preloaded, both with `font-display: swap`. Variable fonts where the design tolerates them — one HTTP request instead of four.

What we cut from the font stack

Most agency stacks ship four to seven font files: regular, medium, bold and italic for both the heading and body family. That is 200–400 KB of WOFF2 before a single byte of content. We pick one variable font for body (a single 60 KB file covers weight 100–900 plus italics) and one display font for headings, both `next/font` self-hosted so they bypass Google's third-party domain and stay on the same HTTP/2 connection as the page. If a brief asks for a fourth weight, we trade it for a CSS `font-weight: bold` synthetic — readers will not notice; Lighthouse will.

JS gating

Zero analytics in the first 100 KB. GA4, Hotjar, ad pixels — all loaded after the page is interactive, behind a consent banner. The hero, navigation and CTAs render without a single line of client JS.

The third-party tax, in numbers

Every UK site we audit ships at least one of: GA4 (87 KB), GTM (47 KB), Hotjar (78 KB), Meta Pixel (62 KB), LinkedIn Insight (39 KB), Intercom widget (320 KB). Most ship four of those. That is 600+ KB of third-party JavaScript executing on the main thread before the user has touched anything. Our approach: every analytics or ad pixel sits behind a `requestIdleCallback` triggered after the page is fully interactive AND consent has been granted. The hero, the navigation, the CTAs are pure HTML and CSS — the JavaScript bundle for the first paint is under 12 KB and serves only the interactive flourishes (the magnetic buttons, the scroll progress bar). Everything else is deferred behind user intent or idle time.

CLS: reserve every box

Every image gets a width/height attribute. Every embed gets an aspect-ratio container. Lazy-loaded sections use a fixed minimum height. CLS comes from optimistic shipping — we ship pessimistically.

The hidden CLS sources

Three things bite people who think they have CLS handled: web fonts (a 0.04 shift when the swap happens — fix with `size-adjust` descriptors in @font-face); cookie banners that animate in after 800ms and push the hero down by 60px (fix: render the banner inside a `position: fixed` shell that overlays rather than reflows); third-party embed widgets (YouTube, TrustBox, Klaviyo signup) that load asynchronously and inflate from zero height — fix by wrapping in an `aspect-ratio` div sized to the embed's final dimensions. We measure CLS once with the chrome DevTools Performance tab on throttled 4G, then audit every dynamic element with the "Layout Shift Regions" overlay.

INP: no synchronous layout

Magnetic buttons, tilt cards, scroll progress — all use transform / opacity, never width / top / margin. The browser stays on the GPU.

INP, expanded — what actually breaks the 200 ms threshold

Five common offenders in order of frequency: (1) heavy event handlers that block the main thread — usually a third-party script reacting to a click and triggering a re-render; (2) React state updates that touch the whole tree because someone forgot a useMemo on a derived value; (3) synchronous reads of `offsetTop` or `scrollTop` during scroll handlers, which force a layout flush; (4) animations that toggle `display: none` instead of `opacity: 0` — the former triggers reflow on every paint; (5) hydration of an over-eager component tree on a server-rendered page. Our default React tree is small — most pages have zero client components in the critical path — so INP usually sits in the 40–80 ms range without effort. Where we need an interaction (form, accordion, tab), we ship a single self-contained client component and keep its handler under 50 lines.

The testing protocol, before handover

We run three measurements on every site before it ships: (1) PageSpeed Insights mobile and desktop on three different page templates (home, deep page, blog), targeting 95+ on Performance; (2) DevTools Performance recording on a throttled "Slow 4G + 6× CPU" profile with the cache disabled, watching the main-thread occupancy from cold load to interactive; (3) field-data check via the CrUX API after 28 days of live traffic, confirming 75th-percentile field LCP/INP/CLS all sit inside the green band. If any of the three fails, the launch is held until it passes. We have not missed the threshold on a same-day build in 11 months.

Hosting choices that quietly help

The host matters more than people think. Static-first hosts that serve from the edge — Vercel, Netlify, Cloudflare Pages — give you a TTFB under 100ms from most UK locations because the HTML is cached at a PoP near the user. Shared-PHP hosts (the £3 / month traditional UK plans) routinely serve TTFB of 600–1,400ms before TLS handshake, which alone can blow your LCP budget on a slow connection. For a Next.js build with `force-static`, we deploy to Vercel UK or Cloudflare; for plain HTML we deploy to Netlify or Hetzner with a Cloudflare front. Either way the HTML is on a CDN within 25ms of any UK city by the time the user requests it.

What this does to conversion, not just rank

The rank story is the headline, but the conversion story is the reason owners keep paying. Across the launches we have tracked, sites that move from a 3.4-second LCP to a sub-1.8-second LCP see lead form completion rates rise by 11–18% with no other change. Bounce rates drop in lockstep. The mechanism is unromantic: people on the move, on a phone, tapping a paid ad — they wait two seconds, not five. Performance work pays for itself on the next ad invoice, never mind the organic uplift.

Result

Every site we ship scores 95+ on PageSpeed Insights mobile, with LCP under 1.8s on slow 4G. We test before handover and re-test 30 days post-launch.

The diagnostic loop when a Vitals score regresses

Even on a tuned site, regressions happen — a marketing team adds a chat widget, an A/B test injects an extra render, a CMS update bundles unexpected polyfills. The standing diagnostic loop has five steps. (1) Confirm the regression in field data, not just lab data — CrUX updates daily; PageSpeed Insights surfaces it the day after the threshold crosses. (2) Identify which page templates are affected — usually it is one route, not the whole site. (3) Open the offending template in DevTools Performance, throttled, with the cache disabled, and record the full load. (4) Look for the new long task on the main thread: who added it, what does it do, can it be deferred. (5) Ship the fix behind a feature flag where possible so the rollback is one toggle if it makes things worse. We run this loop monthly across every client site as part of the ongoing care relationship; for self-managed clients we send a quarterly report flagging anything that has drifted.

The relationship between Vitals and ad performance

Performance bleeds into paid as well as organic. Google Ads ties Quality Score partly to landing-page experience, which is partly Core Web Vitals; Meta Ads weights "page speed" in its ad ranking auction; TikTok Ads has begun publicly weighting LCP in its 2026 ad-quality scoring. A landing page with LCP at 3.5s pays a higher CPM than the same offer at LCP 1.5s, sometimes by 15-25% on competitive auctions. The performance work we do for free as part of the build pays its way through the next quarter's ad invoice on any client running paid media, before the organic uplift is even measurable.

A closing thought on diminishing returns

There is a point at which further performance work stops paying back. Going from a 4-second LCP to a 2-second LCP is transformative; going from 1.8 to 1.4 changes neither rankings nor conversions meaningfully. The sensible target is comfortable green-band on field data with 20% headroom; beyond that, the engineering time is better spent on the next page rather than shaving milliseconds off an already-fast one. Performance is a means, not the product.

Skip the reading

Want it
built for you?
Today.

Most of these articles end with “or you could just brief us and have it shipped by 6 PM”. From £699 one-off.

📚
6
In-depth posts
🚀
5k+
UK businesses launched
8–24h
Average build time
98%
Client satisfaction