How I Reduced LCP from 5s to Under 1 Second on a 10M-User Platform

Introduction
Performance problems rarely come from a single issue.
When we started investigating why our platform had an LCP consistently above 5 seconds, we initially assumed the problem was image optimization. While images were definitely part of the issue, the real bottleneck turned out to be a combination of rendering delays, excessive JavaScript execution, blocking third-party scripts, and poor caching strategy.
This article walks through the exact changes we implemented to bring LCP down to under 1 second on key landing pages.
Understanding the Real Bottleneck
The first mistake many teams make is optimizing blindly.
Before changing anything, we profiled the application using:
- Lighthouse
- Chrome Performance Profiler
- WebPageTest
- Real User Monitoring (RUM)
- Next.js bundle analyzer
The findings were revealing:
- Main hero image loaded too late
- Massive hydration cost from unnecessary client-side rendering
- Third-party scripts blocking the main thread
- Fonts delaying rendering
- Over 1.2MB of unused JavaScript
- API requests cascading sequentially
The platform looked fast internally because developers tested on fast machines and stable connections.
Real-world users had a very different experience.
Step 1: Prioritizing Above-the-Fold Content
The hero section was the LCP element.
Initially, it loaded through a generic image component with lazy loading enabled by default.
That meant the browser delayed fetching the most important visual element.
We changed the implementation to preload the hero image immediately.
<Image
src="/hero.webp"
alt="Platform Hero"
priorityfillsizes="100vw"
/>We also converted all large images to WebP and AVIF formats.
This alone reduced image payload sizes by nearly 70%.
Step 2: Reducing Hydration Cost
We discovered that entire pages were unnecessarily rendered as client components.
Large interactive trees were being hydrated even when most sections were static.
We migrated aggressively toward React Server Components.
Before:
'use client'
export default function HomePage() {
return <HeavyLandingPage />
}After
export default async function HomePage() {
const data = await getHomepageData()
return <LandingPage data={data} />
}Only interactive components remained client-side.
The JavaScript shipped to users dropped dramatically.
Step 3: Eliminating Third-Party Script Damage
Marketing tools were destroying performance.
We found:
- Multiple analytics providers
- Chat widgets
- Heatmaps
- Ad scripts
- Tracking pixels
Several scripts blocked rendering entirely.
We audited every integration and categorized them into:
- Critical
- Delayed
- Remove completely
Many tools were removed entirely because nobody actively used the data.
Others were deferred:
<Script
src="https://example.com/script.js"
strategy="lazyOnload"
/>This reduced Total Blocking Time significantly.
Step 4: Optimizing Fonts Properly
Custom fonts were creating invisible text flashes.
We:
- Self-hosted fonts
- Reduced font weights
- Used font-display: swap
- Preloaded only critical font files
@font-face {
font-family: 'Inter';
src: url('/fonts/inter.woff2') format('woff2');
font-display: swap;
}The difference was immediately noticeable.
Step 5: Fixing API Waterfalls
Several API requests depended on previous responses.
This created sequential waiting.
We refactored requests to execute in parallel.
Before:
const user = await getUser()
const posts = await getPosts(user.id)
const analytics = await getAnalytics(user.id)After
const userPromise = getUser()
const postsPromise = getPosts()
const analyticsPromise = getAnalytics()
const [user, posts, analytics] = await Promise.all([
userPromise,
postsPromise,
analyticsPromise,
])This reduced backend waiting time substantially.
Step 6: Smarter Caching Strategy
We implemented layered caching:
- CDN edge caching
- API response caching
- ISR in Next.js
- Browser cache headers
Example:
export const revalidate = 300Combined with CDN optimizations, many pages became nearly instant.
Results
Final metrics after deployment:

Final Thoughts
Performance engineering is rarely about one magical optimization.
It is usually the result of dozens of small architectural decisions compounded over time.
The biggest lesson from this project was simple:
Measure first. Optimize second.
Most teams waste time solving the wrong bottlenecks.
Once we focused on the actual rendering path users experienced, the improvements became much easier to prioritize.

Autorius
Miracle Kalu
Senior Full Stack Engineer
Patiko tai, ką perskaitėte?
Svarstau vyresniojo inžinieriaus pozicijas ir techninio konsultavimo projektus. Susisiekime.
Susisiekti →Paskelbta 2026 m. gegužės 11 d. · Skaitymo trukmė – 2 min.
Skaitykite toliau
