Code Splitting & Lazy Loading
HardCode splitting breaks your application bundle into smaller chunks loaded on demand, reducing the initial JavaScript download. React.lazy with dynamic import() creates lazy-loaded components, and Suspense provides the loading fallback. This is critical for large applications to maintain fast initial load times.
Interactive Visualization
Key Points
- React.lazy accepts a function returning a dynamic import() promise
- The bundler automatically creates a separate chunk for the lazy component
- Suspense wraps lazy components and displays a fallback during loading
- Route-level splitting is the highest-impact optimization
- React.lazy only supports default exports; use re-export for named exports
- Preloading chunks on hover or route prefetch improves perceived performance
Code Examples
Route-Level Code Splitting
import { lazy, Suspense } from 'react' const Dashboard = lazy(() => import('./Dashboard')) const Settings = lazy(() => import('./Settings')) const Analytics = lazy(() => import('./Analytics')) function LoadingSpinner() { return <div>Loading...</div> } function App({ currentRoute }: { currentRoute: string }) { return ( <Suspense fallback={<LoadingSpinner />}> {currentRoute === 'dashboard' && <Dashboard />} {currentRoute === 'settings' && <Settings />} {currentRoute === 'analytics' && <Analytics />} </Suspense> ) }
Each route component is a separate chunk that loads only when that route is visited, keeping the initial bundle small
Named Export and Preloading
import { lazy, Suspense, useState } from 'react' // Lazy load named export by re-exporting as default const Chart = lazy(() => import('./Charts').then((mod) => ({ default: mod.PieChart })) ) // Preload on hover for instant navigation const preloadSettings = () => import('./Settings') function Nav() { const [showChart, setShowChart] = useState(false) return ( <div> <button onMouseEnter={preloadSettings} onClick={() => { /* navigate to settings */ }} > Settings </button> <button onClick={() => setShowChart(true)}>Show Chart</button> {showChart && ( <Suspense fallback={<div>Loading chart...</div>}> <Chart /> </Suspense> )} </div> ) }
Preloading on hover starts the download before the user clicks, making the transition feel instant
Common Mistakes
- Not wrapping lazy components in a Suspense boundary causing a runtime error
- Splitting too aggressively, creating many tiny chunks that hurt performance with HTTP overhead
- Not handling chunk load failures with an error boundary
Interview Tips
- Explain route-level vs component-level splitting and when each is appropriate
- Know how to handle load failures with error boundaries for lazy components
- Discuss prefetching strategies to improve perceived performance