Virtual Internships · 2023–25
Web Vitals Programme
Was 5.45snow 3.17s
The batch-selection page was the highest-traffic flow in the product and its LCP p75 sat at 5.45s. The instinct with a number like that is to reach for a rewrite. It didn't need one.
What was actually slow
The LCP element was a single SVG the browser couldn’t discover until JavaScript had parsed and rendered. Everything else was contention — Google Analytics and HubSpot competing for the main thread during first paint, and DNS lookups happening serially at the worst possible moment.
The fix
Preload the LCP image with fetchpriority="high" so the browser starts fetching before it knows the DOM. dns-prefetch and preconnect for the third-party origins, with crossoriginon the preconnect so the connection isn’t opened twice. Defer analytics to post-interactive. Move web-vitals initialisation into requestIdleCallback. Add an eager prop to BgImageWithLoader so an already-preloaded image skips its JavaScript load check entirely.
Layout shift
CLS was a separate, duller problem: things arriving late and pushing content down. Reserve the header height, reserve the container, and make skeleton cards exactly 145×325px so the swap to real content moves nothing. That took it under 0.1, and on intern profiles the same approach took CLS p75 from 0.229 to 0.006.
Results
| Measure | Was | Now |
|---|---|---|
| Batch selection — LCP p75 | 5.45s | 3.17s |
| Intern profile — CLS p75 | 0.229 | 0.006 |
| Onboarding — LCP p75 | 9.9s | 8.6s |
| Dashboard — CLS p75 | 0.44 | 0.24 |