PERFORMANCEOCT 20253 MIN READ← INSIGHTS

The Frontend Performance Journey Behind Simplilearn's Next.js Migration

LCPWEB-VITALSNEXTJSREACT

The Diagnosis

Simplilearn runs one of India's largest edtech platforms, with over a million organic visits per month. When the engineering team started evaluating a migration from our legacy React application (internally called the legacy frontend app) to Next.js (the frontend monorepo), the case wasn't just developer experience — it was performance.

the legacy frontend app had accumulated years of architectural debt. Course landing pages loaded slowly on real mobile devices under real network conditions. The diagnosis phase covered three measurement approaches: Chrome DevTools with CPU and network throttling to simulate typical user conditions, WebPageTest from real mobile devices, and field data from Chrome UX Report (CrUX) to understand what actual users were experiencing rather than lab ideals.

The lab and field data painted the same picture. Hero images on course landing pages were loading without optimization, render-blocking scripts were competing for early bandwidth, and the client-side rendering model meant users stared at incomplete pages while JavaScript executed. The question wasn't whether performance needed work — it was how to address it systematically during a migration rather than patching the old system indefinitely.

Image Optimization

One of the most impactful immediate wins from the Next.js migration was gaining access to a proper image optimization pipeline. In the legacy frontend app, images uploaded through the CMS went to production without any processing — full-resolution assets served over origin with no format conversion, no responsive sizing, no compression tuning.

The Next.js <Image> component, paired with a CDN handling on-the-fly optimization, changed this fundamentally: WebP and AVIF conversion based on browser support, responsive sizing via srcset so mobile devices stop downloading desktop-resolution images, and automatic compression.

Getting the implementation right required care. The common mistake is treating <Image> as a drop-in for <img> with fixed pixel dimensions — that preserves the old behavior. The correct pattern for responsive layouts uses fill with a sized container:

code
// Before: fixed size, downloads oversized image on mobile
<Image src={hero.src} width={1200} height={600} alt={hero.alt} />

// After: fill with proper container and priority hint
<div className="relative aspect-video w-full">
  <Image
    src={hero.src}
    fill
    sizes="(max-width: 768px) 100vw, 50vw"
    alt={hero.alt}
    priority
  />
</div>

The priority prop is not optional for above-the-fold images. It instructs Next.js to emit a <link rel="preload"> in the document head, so the browser begins fetching the LCP candidate during the earliest phase of page load rather than after JavaScript has parsed and rendered the component tree.

Font Loading Strategy

the legacy frontend app loaded multiple font weights from Google Fonts via a <link> tag in the document head. The hidden cost: Google Fonts serves a CSS file that then triggers a second round of network requests for the actual font binaries — a two-hop waterfall before any custom text can render.

the frontend monorepo switched to next/font with locally hosted font files and a system font stack fallback. The next/font module handles font-display: swap automatically, so text renders immediately in the fallback font while the custom typeface loads. Invisible text during load is eliminated by default.

To minimize layout shift from the font swap, the system font fallback was tuned with matching ascent-override and line-gap-override values, so the line heights and spacing are close enough that the visual jump when the custom font loads is imperceptible. CLS from font loading dropped to effectively zero.

Bundle Size and Code Splitting

Next.js's build tooling made it straightforward to audit bundle composition in ways that the legacy frontend app's setup made difficult. Running the build with @next/bundle-analyzer revealed the kinds of issues that accumulate invisibly over time in a long-lived codebase: dependencies imported via copy-paste from other contexts and never cleaned up, libraries pulled in wholesale when only a single function was needed.

The specific categories of wins from this audit:

Unused dependencies: A rich text editor library had found its way into a marketing landing page component — an artifact of a previous refactor. It was loading for every visitor on a page that had no editing functionality.

Tree-shaking failures: Libraries like date-fns imported as namespace imports (import * as dateFns) rather than named imports defeat tree-shaking. The fix is mechanical; the impact compounds across a codebase.

Dynamic imports for below-the-fold content: Components like testimonials, FAQ accordions, and video embeds don't need to be in the initial bundle. next/dynamic with ssr: false defers their JavaScript out of the critical path entirely — users see the above-fold content faster and the deferred components load as they scroll.

SSR, SSG, and the Uptime Story

The architectural shift that had the most operational impact was moving from a purely client-side rendering model to Next.js's hybrid SSR/SSG capabilities. In the legacy frontend app, most pages required the full JavaScript execution cycle before rendering content — which meant that infrastructure issues, API slowdowns, or JavaScript errors left users with blank or broken pages.

the frontend monorepo's SSG-first approach for content-heavy pages like course landing pages meant pre-rendered HTML served directly from the CDN edge. Pages load with full content even before any client-side JavaScript executes. SSR for dynamic, user-specific content maintained personalization while still shipping a meaningful initial HTML payload.

The result that we can point to directly: platform uptime improved from 95% to 99%. That 4-percentage-point difference is the gap between a platform that's unavailable roughly 18 days per year and one that's unavailable roughly 3.5 days per year. For an edtech platform with learners in active courses, that difference matters.

What the Migration Taught Us

Systematic diagnosis precedes systematic improvement. Without the measurement phase — real device testing, field data, bundle analysis — it's easy to optimize the wrong things confidently.

Image handling and font loading are high-leverage starting points for most content-heavy sites. They're often the largest contributors to poor Core Web Vitals and they're addressable without restructuring business logic.

The framework upgrade wasn't magic. Every win in the frontend monorepo came from making deliberate choices that the new capabilities made possible: choosing fill over fixed dimensions, locally hosting fonts instead of relying on third-party CDN hops, running bundle analysis and actually acting on it, choosing SSG for the pages where it fit. The technology made these choices easier. The outcomes still required intent.