Dynamic Imports and Prefetching Strategies0%

Dynamic Imports and Prefetching Strategies

Beginner12 min read•Updated: Jul 11, 2026
Study MaterialsCheatsheet

Dynamic Imports and Prefetching Strategies

Code splitting saves bandwidth on initial load, but naive splitting introduces navigation latency: when a user clicks a route or modal button, they must pause while the browser makes a round-trip network request to fetch the JavaScript chunk. Prefetching strategies bridge this gap by intelligently downloading code chunks ahead of user interaction.


1. The Latency Gap of Lazy Loading

Consider this user timeline without prefetching:

  1. 1
    User hovers over "Analytics" link (0ms).
  2. 2
    User clicks "Analytics" link (300ms).
  3. 3
    Browser initiates network request for analytics.chunk.js (305ms).
  4. 4
    Network roundtrip and bundle execution takes 400ms.
  5. 5
    User stares at a loading spinner until 705ms.

With Hover/Intent Prefetching, the chunk starts downloading at 0ms. By 300ms, the chunk is already cached in browser memory, rendering the view immediately on click.


2. Programmatic Dynamic Prefetching

Because dynamic import() returns a standard JavaScript Promise, you can trigger chunk evaluation on standard DOM events:

TSX
import React, { Suspense, lazy } from "react";
 
// Factory function returning dynamic import Promise
const loadAnalyticsModule = () => import("./pages/AnalyticsPage");
 
// React.lazy references the same module factory
const AnalyticsPage = lazy(loadAnalyticsModule);
 
export function NavigationLink() {
const [showAnalytics, setShowAnalytics] = React.useState(false);
 
// Prefetch immediately when user hovers or focuses the trigger
const prefetchAnalytics = () => {
// Calling the factory triggers network fetch and caches module in browser cache
loadAnalyticsModule();
};
 
return (
<div>
<button
onMouseEnter={prefetchAnalytics}
onFocus={prefetchAnalytics}
onClick={() => setShowAnalytics(true)}
className="px-4 py-2 bg-emerald-600 text-white rounded hover:bg-emerald-500 font-medium"
>
View Analytics (Hover to Prefetch)
</button>
 
{showAnalytics && (
<Suspense fallback={<div>Loading Analytics...</div>}>
<AnalyticsPage />
</Suspense>
)}
</div>
);
}

3. Webpack & Rollup Magic Comments

Modern bundlers support declarative prefetching annotations embedded inside dynamic import() statements:

Webpack Magic Comments

TSX
const AdminReport = lazy(() =>
import(
/* webpackPrefetch: true */
/* webpackChunkName: "admin-report" */
"./AdminReport"
)
);
  • webpackPrefetch: true: Injects <link rel="prefetch" href="admin-report.chunk.js"> into the document <head>, downloading the chunk during browser idle time after the parent page finishes loading.
  • webpackPreload: true: Injects <link rel="preload">, instructing the browser to download the chunk with high priority alongside the parent page.

4. Viewport Intersection Prefetching

For bottom-of-page sections or long infinite feeds, prefetch chunks when a scroll anchor enters the viewport using IntersectionObserver:

TSX
import React, { useEffect, useRef, lazy, Suspense, useState } from "react";
 
const loadFooterChart = () => import("./components/ComplexFooterChart");
const ComplexFooterChart = lazy(loadFooterChart);
 
export function InfinitePage() {
const sentinelRef = useRef<HTMLDivElement>(null);
const [renderChart, setRenderChart] = useState(false);
 
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// Target is close to viewport: prefetch bundle!
loadFooterChart();
observer.disconnect();
}
});
},
{ rootMargin: "200px" } // Trigger 200px before scrolling into view
);
 
if (sentinelRef.current) {
observer.observe(sentinelRef.current);
}
 
return () => observer.disconnect();
}, []);
 
return (
<div className="space-y-12">
<div className="h-[1200px] bg-slate-900 p-8 text-white">
Scroll down to trigger background chunk prefetch...
</div>
 
<div ref={sentinelRef} />
 
<button
onClick={() => setRenderChart(true)}
className="px-4 py-2 bg-cyan-600 text-white rounded"
>
Display Chart
</button>
 
{renderChart && (
<Suspense fallback={<div>Mounting chart...</div>}>
<ComplexFooterChart />
</Suspense>
)}
</div>
);
}

5. Network-Aware Prefetching

Never waste cellular mobile data. Use navigator.connection to disable aggressive prefetching on slow connections or when Data Saver mode is enabled:

TypeScript
export function shouldPrefetch(): boolean {
if (typeof window === "undefined") return false;
 
const conn = (navigator as any).connection;
if (!conn) return true;
 
// Don't prefetch if user has Save-Data header or slow 2G/3G network
if (conn.saveData) return false;
if (conn.effectiveType === "2g" || conn.effectiveType === "slow-2g") return false;
 
return true;
}

Interactive Quiz

Practice Quiz

Question 1
How does hover-based prefetching reduce perceived navigation latency?
Question 2
What is the difference between webpackPrefetch and webpackPreload?
Question 3
Why is it recommended to inspect navigator.connection before running programmatic prefetch scripts?
Question 4
How does executing loadComponent() before React mounts <LazyComponent /> avoid the Suspense fallback?
Question 5
In IntersectionObserver prefetching, what does the rootMargin: "200px" property accomplish?
Next Lesson

Analyzing Bundle Sizes with Rollup and Webpack Visualizer

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext