A practical, step-by-step guide demonstrating real-world Core Web Vitals optimization techniques on a modern React 19 web application.
This repository shows the journey from an unoptimized web application to a blazing-fast, 100/100 Lighthouse score.
| Version | Branch | Cloudflare Pages URL | Description |
|---|---|---|---|
| Unoptimized (Baseline) | raw |
raw.everest-landing.pages.dev | Unoptimized baseline with buildless in-browser Babel transpilation & network request waterfalls |
| Optimized (Clean) | optimized-clean |
everest-landing-optimized-clean.pages.dev | Fully optimized with 7 atomic performance commits |
| Metric | Target | Bottleneck in Baseline | Solution Applied | Step Tag |
|---|---|---|---|---|
| TTFB / Assets | < 0.8s |
Unminified assets, synchronous blocking scripts, uncompressed bundles | Terser, CSSMinimizer, Async CSS plugin, defer scripts | step-1-build |
| FCP (First Contentful Paint) | < 1.8s |
Blank white screen during JS parse, blocking external web fonts | Inline HTML/CSS branding skeleton, font preloading (<link rel="preload">) |
step-2-fcp |
| LCP (Largest Contentful Paint) | < 2.5s |
Heavy uncompressed JPEG hero images, synthetic delays | Converted to WebP format, <link rel="preload"> on LCP image, removed synthetic delays |
step-3-lcp |
| Bundle Size / CSS | < 100KB |
Monolithic bundle, all offscreen components loaded upfront | React.lazy() + Suspense, modularized CSS chunks, Babel ES module preservation |
step-4-codesplit |
| Network Priority | - | All 30+ offscreen images downloaded eagerly on page load | Native loading="lazy" on all offscreen images |
step-5-lazy-images |
| CLS (Cumulative Layout Shift) | < 0.1 |
Dynamic hiring banner injected asynchronously causing page reflow | Absolute positioning and layout reservation for top banner | step-6-cls |
| INP (Interaction to Next Paint) | < 200ms |
Synchronous heavy filtering of 100+ DOM cards blocking typing thread | React 19 useTransition for non-blocking UI rendering + DOM render limits (DISPLAY_LIMIT) |
step-7-inp |
You can jump to any optimization stage in the presentation using Git tags:
# Baseline unoptimized state
git checkout master
# Step 1: Minification, Compression, Async CSS & Defer
git checkout step-1-build
# Step 2: Inline HTML Skeleton & Font Preload (FCP)
git checkout step-2-fcp
# Step 3: WebP Hero Images & Asset Preloading (LCP)
git checkout step-3-lcp
# Step 4: Code Splitting & Modular CSS (Bundle Size)
git checkout step-4-codesplit
# Step 5: Native Lazy Loading for Offscreen Images
git checkout step-5-lazy-images
# Step 6: Layout Shift Elimination (CLS)
git checkout step-6-cls
# Step 7: React 19 useTransition & Render Limits (INP)
git checkout step-7-inp
# Switch back to the fully optimized clean branch
git checkout optimized-clean- Node.js: >= 18.0.0
- npm: >= 9.0.0
# Navigate to the app directory
cd everest-landing
# Install dependencies
npm install
# Start local development server (with HMR)
npm run devThe dev server will be running at http://localhost:5173.
# Run production Webpack build
npm run build
# Preview production build locally (with compression & production settings)
npm run servedist/
├── index.html # Inlined FCP skeleton & preloads
├── _headers # Cloudflare Cache-Control headers
├── css/
│ ├── main.[hash].css # Critical above-the-fold styles (12 KB)
│ ├── 391.[hash].chunk.css # Testimonials component styles (2.0 KB)
│ ├── 714.[hash].chunk.css # Team component styles (5.0 KB)
│ ├── 772.[hash].chunk.css # Contact component styles (1.9 KB)
│ └── 988.[hash].chunk.css # OfficeLocations styles (2.4 KB)
├── js/
│ ├── main.[hash].js # Critical application entry (13 KB)
│ ├── 179.[hash].js # Shared vendor chunk (React/ReactDOM) (185 KB)
│ ├── 391.[hash].chunk.js # Lazy Testimonials component (3.4 KB)
│ ├── 714.[hash].chunk.js # Lazy Team component (5.8 KB)
│ ├── 772.[hash].chunk.js # Lazy Contact component (5.1 KB)
│ └── 988.[hash].chunk.js # Lazy OfficeLocations component (2.5 KB)
└── images/ # Optimized WebP assets & SVGs
Deploy scripts automatically checkout the respective branch, build the production bundle, attach Cloudflare _headers, and publish via Wrangler (compatible with Windows, macOS, and Linux):
# Deploy unoptimized baseline (master)
npm run deploy
# Deploy clean optimized version (optimized-clean)
npm run deploy:optimized-clean
# Deploy original optimized backup branch (optimized)
npm run deploy:optimizedNote
The _headers file specifies Cache-Control: no-transform to prevent Cloudflare from applying automatic edge transformations (like Auto-Minify or Polish), ensuring benchmarks measure your exact codebase.
- Production Mode: Always run tests against the production build (
npm run serveor the deployed Cloudflare Pages URL). - Lighthouse Audit:
- Open Chrome DevTools (
F12). - Switch to the Lighthouse tab.
- Select Mode: Navigation, Device: Mobile (or Desktop), and Categories: Performance.
- Click Analyze page load.
- Open Chrome DevTools (
- Simulating Throttling:
- In DevTools Network tab, set Throttling to Fast 4G or Slow 4G.
- In DevTools Performance tab, set CPU Throttling to 4x slowdown or 6x slowdown to observe INP improvements with
useTransition.
Created for Everest Engineering Tech Talk on Web Performance.
Author: ShyamKumar-Crafts <shyam.kumar@everest.engineering>