This article is based on real-world experience optimizing web performance and the strategies discussed here can be adapted for various web applications facing similar challenges.
As a frontend engineer at an events intelligence AI platform, I faced a crucial challenge: improving the performance of our most visited page. This experience taught me valuable lessons about optimizing Core Web Vitals and enhancing user experience. Here's what I learned along the way.
Understanding Core Web Vitals: The Three Pillars
Before diving into specific optimizations, it's essential to understand what we're measuring. Core Web Vitals consist of three key metrics that Google uses to evaluate user experience:
1. Largest Contentful Paint (LCP): Measures loading performance
- Good: ≤ 2.5 seconds
- Needs Improvement: ≤ 4 seconds
- Poor: > 4 seconds
2. First Input Delay (FID): Measures interactivity
- Good: ≤ 100 milliseconds
- Needs Improvement: ≤ 300 milliseconds
- Poor: > 300 milliseconds
3. Cumulative Layout Shift (CLS): Measures visual stability
- Good: ≤ 0.1
- Needs Improvement: ≤ 0.25
- Poor: > 0.25
Our Core Web Vitals report categorized URLs into these three status groups, helping us prioritize our optimization efforts.
Technical Deep Dive: Performance Optimizations
1. Font Loading Strategy
Implemented a comprehensive font loading strategy that included several technical optimizations:
<!-- Preload critical fonts -->
<link
rel="preload"
href="/fonts/primary-font.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<!-- Font-face definitions with size-adjust -->
<style>
@font-face {
font-family: 'Primary Font';
font-display: optional;
src: url('/fonts/primary-font.woff2') format('woff2');
size-adjust: 97.5%; /* Matched to fallback font */
}
/* Fallback font metrics matching */
@font-face {
font-family: 'Arial Fallback';
src: local('Arial');
}
</style>
2. Resource Optimization
Resource blocking is about controlling how and when your web page loads different resources (like scripts, stylesheets, images, fonts) to improve performance. The key idea is to block (delay) non-essential resources while prioritizing critical ones.
<!-- Only loads on mobile devices -->
<link rel="stylesheet" href="/mobile.css" media="screen and (max-width: 600px)">
<!-- async: Load in parallel, execute when ready -->
<script async src="/non-critical.js"></script>
<!-- defer: Load in parallel, execute after HTML parsing -->
<script defer src="/can-wait.js"></script>
<!-- Tell browser to preload critical resources -->
<link rel="preload" href="/critical-font.woff2" as="font" crossorigin>
<!-- Connect early to required origins -->
<link rel="preconnect" href="https://api.example.com">
<!-- Load resource only when needed-->
if (someCondition) {
const script = document.createElement('script');
script.src = '/feature.js';
document.head.appendChild(script);
}
Measurable Results
Here's how we improved each Core Web Vital and the specific optimizations that made it possible:
1. Largest Contentful Paint (LCP)
Before: 4.2s (Poor) → After: 2.1s (Good)
The dramatic improvement in LCP came from identifying and fixing several critical issues:
- We discovered our hero image (1.2MB) was blocking the critical rendering path. By implementing responsive images and modern formats (WebP/AVIF), we reduced the image size by 70%.
- Moving our web fonts to use
font-display: optional
prevented font-loading delays. - Implementing preload hints for critical assets helped prioritize important resources.
2. Cumulative Layout Shift (CLS)
Before: 0.28 (Poor) → After: 0.08 (Good)
The major reduction in layout shifts came from addressing three main culprits:
- Font swapping was causing significant shifts. We fixed this by implementing font
size-adjust
and matching fallback fonts metrics. - Dynamic content loading (like attendee lists) was pushing content down. We implemented skeleton screens and reserved space for dynamic content.
3. First Input Delay (FID)
Before: 250ms (Needs Improvement) → After: 85ms (Good)
The improvement in interactivity came from:
- Breaking up long-running JavaScript tasks that were blocking the main thread. We identified these using the Performance panel in Chrome DevTools.
- Moving heavy computations (like filtering) to Web Workers.
- Implementing proper event delegation instead of attaching hundreds of individual event listeners. e.g Use useCallback to memoize the event handler
Additional Critical Improvements
1. First Contentful Paint (FCP)
- Improved from 2.8s to 1.4s
- Achieved by inlining critical CSS and deferring non-critical styles
- Implemented route-based code splitting to reduce initial JavaScript payload
2. Total Blocking Time (TBT)
- Decreased from 600ms to 200ms
- Achieved by deferring non-critical JavaScript execution
- Implemented requestIdleCallback for non-essential operations
Key Technical Takeaways
1. Implement Resource Hints Strategically
- Use
preload
for critical resources - Implement
prefetch
for resources needed later - Enable
preconnect
for third-party domains
2. Optimize Font Loading
- Use
size-adjust
and metric overrides - Implement fallback font matching
- Leverage
font-display
strategies
3. Monitor Performance Continuously
- Use Performance Observer API
- Track Core Web Vitals in real-time
- Set up automated performance budgets
Looking Forward
Web performance optimization is an ongoing journey. We continue to monitor our Core Web Vitals and make adjustments as needed. The improvements we've made to our page have not only enhanced user experience but also contributed to better engagement metrics across the platform.
Top comments (0)