Monday, June 30, 2025

How We Cut Load Time from 6 Seconds to Under 1

optimization

Last quarter, our flagship product’s dashboard had ballooned to 6.2 seconds average load time on a mid-tier laptop with a normal 4G connection. Users were frustrated, sales teams were fielding complaints, and analytics showed a 21% bounce rate on initial login.

We decided to commit a full performance sprint to fix it, with a goal of sub-1 second perceived load. Here’s exactly what we did, line by line, decision by decision.

Step 1: Get the Evidence

We ran:

Lighthouse — to benchmark first paint, LCP, and blocking time
WebPageTest — to verify real network throttling
Chrome DevTools — for the flamegraph and request waterfall

The evidence:

  • 3.4MB of JS bundles shipping on the initial route

  • 1.2MB of images in hero sections, no compression

  • 1.1s of third-party marketing scripts blocking main thread

  • 22 separate API calls to hydrate dashboard widgets

  • CSS (via a giant Tailwind build) blocking render with no code splitting

Step 2: Slice the JavaScript

We migrated from Create React App to Next.js for better dynamic routing. Instead of shipping the entire dashboard on first load, we used next/dynamic to load feature panels on demand.

Before:

import DashboardStats from './DashboardStats';
import DashboardCharts from './DashboardCharts';

export default function Dashboard() {
  return (
    <>
      <DashboardStats />
      <DashboardCharts />

After (Next.js with dynamic imports):

import dynamic from 'next/dynamic';

const DashboardStats = dynamic(() => import('./DashboardStats'), {
  loading: () => <div>Loading stats...</div>,
});
const DashboardCharts = dynamic(() => import('./DashboardCharts'), {
  loading: () => <div>Loading charts...</div>,
});

export default function Dashboard() {
  return (
    <>
      <DashboardStats />
      <DashboardCharts />

Impact: main bundle went from 3.4MB → 950KB.

Action taken:

  • Migrated to Next.js with dynamic() route-based code splitting

  • Split major feature routes into separate chunks

  • Removed Moment.js (600KB) in favor of Day.js (10KB)

  • Pruned lodash usage with explicit imports (import pick from 'lodash/pick')

Result:
→ JS bundle shrank from 3.4MB to 950KB compressed

Step 3: Fix the Images

Our hero images were 2000px-wide JPEGs, served to every device. We switched to WebP with CloudFront and used srcset.

Before:

<img src="/images/hero.jpg" width="2000" />

After:

<img
  src="/images/hero-800.webp"
  srcset="/images/hero-400.webp 400w,
          /images/hero-800.webp 800w,
          /images/hero-1600.webp 1600w"
  sizes="(max-width: 600px) 400px, 800px"
  width="800"
  alt="Product dashboard"
/>

Plus, we added Sharp to compress images at build time:


Impact: hero image size dropped from 600KB → 120KB.

Action taken:

  • Added WebP generation via Sharp in our build pipeline

  • Used srcset for responsive images

  • Added lazy loading for below-the-fold assets

  • Shifted CDN from vanilla S3 to CloudFront with brotli compression

Result:
→ Image payload dropped from 1.2MB to 320KB, no visible quality loss

Step 4: Optimize the CSS

We had a giant Tailwind CSS file loaded globally. Instead, we used critical CSS inline and deferred the rest:

In _document.js:

<style
  dangerouslySetInnerHTML={{
    __html: criticalCssFromBuildTool,
  }}
/>

Then, for the remainder:

<link
  rel="stylesheet"
  href="/static/main.css"
  media="print"
  onload="this.media='all'"
/>

Impact: CSS blocking dropped from 600ms → 80ms.

Action taken:

  • Added @tailwindcss/purge to remove unused classes

  • Split Tailwind’s output by route

  • Loaded critical CSS inline for above-the-fold content

  • Deferred the rest of the CSS with media="print" + onload swap

Result:
→ CSS blocking time reduced from 600ms to 80ms

Step 5: Consolidate API Calls

We had 22 independent API requests on first paint. We rewrote them as a single GraphQL query:

Before (pseudo-code):


After (GraphQL):


In React:

Impact: reduced first-load data requests from 22 → 4, massively cutting network overhead.

Action taken:

  • Audited queries in the dashboard with product managers

  • Collapsed multiple endpoints into a single GraphQL query

  • Added server-side caching for rarely changing metadata

  • Added stale-while-revalidate caching on the client

Step 6: Audit Third-Party Scripts

We found 11 third-party scripts running on page load. Many were legacy.

Steps taken:

✅ Moved non-critical tools (e.g., Hotjar) to on-demand loading
✅ Removed abandoned chat widgets
✅ Added a CI budget test for total script weight

In index.html:

<script async src="https://analytics.com/script.js"></script>
<script defer src="https://marketing.com/track.js"></script>

And moved expensive scripts behind a user click:


Impact: third-party blocking time dropped from 1.1s → 200ms.

The Measured Outcome

After deploying these changes:

✅ First Contentful Paint: 0.9s (was 4.3s)
✅ Largest Contentful Paint: 1.2s (was 6.2s)
✅ Total blocking time: 90% reduction
✅ JavaScript total: under 1MB
✅ Images total: under 400KB
✅ API latency: median response 220ms (down from 1.4s)

Customer feedback improved dramatically, conversion on the dashboard’s upsell CTA rose by 15%, and support tickets about “the app is slow” went to near zero.

Final Reflections

Speed improvements are never “one fix.” They’re a systematic series of engineering decisions — and real code:

  • Dynamic routing

  • Image pipelines

  • GraphQL batching

  • Critical path CSS

  • Smarter third-party loading

If you’re fighting 6-second loads, measure carefully, prioritize ruthlessly, and verify every change in production..

NEVER MISS A THING!

Subscribe and get freshly baked articles. Join the community!

Join the newsletter to receive the latest updates in your inbox.

Footer Background

About Cerebrix

Smarter Technology Journalism.

Explore the technology shaping tomorrow with Cerebrix — your trusted source for insightful, in-depth coverage of engineering, cloud, AI, and developer culture. We go beyond the headlines, delivering clear, authoritative analysis and feature reporting that helps you navigate an ever-evolving tech landscape.

From breaking innovations to industry-shifting trends, Cerebrix empowers you to stay ahead with accurate, relevant, and thought-provoking stories. Join us to discover the future of technology — one article at a time.

2025 © CEREBRIX. Design by FRANCK KENGNE.

Footer Background

About Cerebrix

Smarter Technology Journalism.

Explore the technology shaping tomorrow with Cerebrix — your trusted source for insightful, in-depth coverage of engineering, cloud, AI, and developer culture. We go beyond the headlines, delivering clear, authoritative analysis and feature reporting that helps you navigate an ever-evolving tech landscape.

From breaking innovations to industry-shifting trends, Cerebrix empowers you to stay ahead with accurate, relevant, and thought-provoking stories. Join us to discover the future of technology — one article at a time.

2025 © CEREBRIX. Design by FRANCK KENGNE.

Footer Background

About Cerebrix

Smarter Technology Journalism.

Explore the technology shaping tomorrow with Cerebrix — your trusted source for insightful, in-depth coverage of engineering, cloud, AI, and developer culture. We go beyond the headlines, delivering clear, authoritative analysis and feature reporting that helps you navigate an ever-evolving tech landscape.

From breaking innovations to industry-shifting trends, Cerebrix empowers you to stay ahead with accurate, relevant, and thought-provoking stories. Join us to discover the future of technology — one article at a time.

2025 © CEREBRIX. Design by FRANCK KENGNE.