Dynamic Imports
Dynamic Imports
Load JavaScript modules on demand instead of upfront.
Basic Dynamic Import
// Static import (loaded upfront)
import { heavyFunction } from './utils';
// Dynamic import (loaded on demand)
const module = await import('./utils');
module.heavyFunction();
React.lazy
import { lazy, Suspense } from 'react';
// Dynamic component import
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<Spinner />}>
<HeavyComponent />
</Suspense>
);
}
Named Exports
// Component with named exports
// HeavyComponent.js
export function ComponentA() { ... }
export function ComponentB() { ... }
// Dynamic import with named exports
const { ComponentA } = await import('./HeavyComponent');
// React.lazy with named export
const ComponentA = lazy(() =>
import('./HeavyComponent').then(module => ({
default: module.ComponentA,
}))
);
Conditional Loading
function App({ showAdvanced }) {
const [AdvancedComponent, setAdvancedComponent] = useState(null);
useEffect(() => {
if (showAdvanced && !AdvancedComponent) {
import('./AdvancedComponent').then(module => {
setAdvancedComponent(() => module.default);
});
}
}, [showAdvanced, AdvancedComponent]);
return (
<div>
<BasicComponent />
{AdvancedComponent && <AdvancedComponent />}
</div>
);
}
Webpack Magic Comments
// Prefetch during idle time
const module = import(
/* webpackChunkName: "dashboard" */
/* webpackPrefetch: true */
'./Dashboard'
);
// Preload during current load
const module = import(
/* webpackChunkName: "settings" */
/* webpackPreload: true */
'./Settings'
);
Route-Based Splitting
Route-Based Splitting
Split code based on routes for optimal loading.
React Router Implementation
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
// Lazy load routes
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
// Loading component
const PageLoader = () => (
<div className="page-loader">
<Spinner />
<p>Loading page...</p>
</div>
);
function App() {
return (
<BrowserRouter>
<Layout>
<Suspense fallback={<PageLoader />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
</Layout>
</BrowserRouter>
);
}
Prefetching Routes
import { Link } from 'react-router-dom';
function NavLink({ to, children }) {
const prefetch = () => {
// Prefetch route component
import(`./pages/${to.slice(1) || 'Home'}`);
};
return (
<Link to={to} onMouseEnter={prefetch}>
{children}
</Link>
);
}
Layout Groups
// Shared layout for auth pages
const AuthLayout = lazy(() => import('./layouts/AuthLayout'));
const Login = lazy(() => import('./pages/auth/Login'));
const Register = lazy(() => import('./pages/auth/Register'));
// Shared layout for dashboard
const DashboardLayout = lazy(() => import('./layouts/DashboardLayout'));
const Overview = lazy(() => import('./pages/dashboard/Overview'));
const Analytics = lazy(() => import('./pages/dashboard/Analytics'));
function App() {
return (
<Suspense fallback={<PageLoader />}>
<Routes>
<Route element={<AuthLayout />}>
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
</Route>
<Route element={<DashboardLayout />}>
<Route path="/dashboard" element={<Overview />} />
<Route path="/dashboard/analytics" element={<Analytics />} />
</Route>
</Routes>
</Suspense>
);
}
Component Splitting
Component Splitting
Split individual components for on-demand loading.
Heavy Component Loading
import { useState, lazy, Suspense } from 'react';
// Chart library loaded only when needed
const Chart = lazy(() => import('./Chart'));
const DataGrid = lazy(() => import('./DataGrid'));
const RichTextEditor = lazy(() => import('./RichTextEditor'));
function Dashboard() {
const [activeTab, setActiveTab] = useState('overview');
return (
<div>
<TabList>
<Tab onClick={() => setActiveTab('overview')}>Overview</Tab>
<Tab onClick={() => setActiveTab('analytics')}>Analytics</Tab>
<Tab onClick={() => setActiveTab('settings')}>Settings</Tab>
</TabList>
<Suspense fallback={<TabLoader />}>
{activeTab === 'overview' && <OverviewTab />}
{activeTab === 'analytics' && (
<div>
<Chart data={analyticsData} />
<DataGrid rows={tableData} />
</div>
)}
{activeTab === 'settings' && <SettingsTab />}
</Suspense>
</div>
);
}
Feature Flags
function App({ features }) {
return (
<div>
<Header />
<main>
<Suspense fallback={<Spinner />}>
{features.dashboard && (
<LazyRoute path="/dashboard" component="./Dashboard" />
)}
{features.advanced && (
<LazyRoute path="/advanced" component="./Advanced" />
)}
</Suspense>
</main>
</div>
);
}
// Dynamic route component
function LazyRoute({ path, component }) {
const Component = lazy(() => import(component));
return (
<Route path={path} element={<Component />} />
);
}
Loading States
// Skeleton loader for split components
function ComponentLoader() {
return (
<div className="component-skeleton">
<div className="skeleton-header" />
<div className="skeleton-content">
<div className="skeleton-line" />
<div className="skeleton-line" />
<div className="skeleton-line" />
</div>
</div>
);
}
// Error boundary for failed loads
class LazyErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return (
<div className="load-error">
<p>Failed to load component</p>
<button onClick={() => window.location.reload()}>
Reload
</button>
</div>
);
}
return this.props.children;
}
}
// Usage
<LazyErrorBoundary>
<Suspense fallback={<ComponentLoader />}>
<LazyComponent />
</Suspense>
</LazyErrorBoundary>
Practice Problems
Create a reusable React component implementing Code Splitting. Include proper state management and accessibility.
Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where neededWrite unit and integration tests for Code Splitting using React Testing Library.
Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility testsOptimize Code Splitting for performance. Consider memoization, code splitting, and bundle size.
Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysisQuiz
1. What is code splitting?
2. What does React.lazy do?
3. Why use route-based splitting?
4. What is webpackPrefetch?
5. Why use Suspense with lazy loading?
Flashcards
Question
What is code splitting?
Click to reveal answer
Answer
Breaking your bundle into smaller chunks that are loaded on demand.
Question
How does React.lazy work?
Click to reveal answer
Answer
It dynamically imports a component when it's first rendered, enabling code splitting.
Question
What is route-based splitting?
Click to reveal answer
Answer
Splitting code based on routes, loading only the JavaScript needed for the current page.
Question
What is the purpose of Suspense?
Click to reveal answer
Answer
To show a fallback UI while lazy components are loading.
Question
What is Code Splitting?
Click to reveal answer
Answer
Code Splitting is a key concept in frontend development.
Revision Notes
Key Takeaways
- 1.Code splitting reduces initial bundle size by loading code on demand
- 2.React.lazy and Suspense enable component-level code splitting
- 3.Route-based splitting loads only code for the current page
- 4.Prefetching improves perceived performance
- 5.Always handle loading and error states for lazy components
Interview Tips
- •Explain how React.lazy and Suspense work together
- •Discuss when to use route vs component splitting
- •Know how to handle errors in lazy loaded components
Cheat Sheet
Code Splitting Cheat Sheet
React.lazy
const Component = lazy(() => import('./Component'));
<Suspense fallback={<Spinner />}>
<Component />
</Suspense>
Route Splitting
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
Prefetching
// During idle time
import(/* webpackPrefetch: true */ './Component');
// On hover
const prefetch = () => import('./Component');
Error Handling
- Use Error Boundary around Suspense
- Show retry option on failure