React.lazy() — is a function that enables dynamic loading of React components. It implements the “lazy loading” pattern and automatically splits application code into separate bundles (code splitting).
Basic usage:
// Instead of regular import
import HeavyComponent from './HeavyComponent';
// Use dynamic import
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
// Must wrap in Suspense
function App() {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</React.Suspense>
);
}
React.lazy() was introduced in React 16.6 and provides a built-in way for code splitting at the component level. This allows loading components only when they are actually needed, reducing the initial bundle size.
// React.lazy takes a function that returns a Promise
const LazyComponent = React.lazy(() => import('./LazyComponent'));
// import() returns a Promise that resolves toa& module
// The module must export a React component as default
// LazyComponent.jsx
// ✅ Correct: default export
export default function LazyComponent() {
return <div>I load lazily!</div>;
}
// ❌ Incorrect: named export
export const LazyComponent = () => {
return <div>This won't work</div>;
};
// Workaround for named exports
const LazyComponent = React.lazy(() =>
import('./components').then(module => ({
default: module.NamedComponent
}))
);
function App() {
return (
<div>
{/* ❌ Error: LazyComponent must be inside Suspense */}
<LazyComponent />
{/* ✅ Correct: use Suspense */}
<React.Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</React.Suspense>
</div>
);
}
// Can wrap multiple lazy components
function Dashboard() {
return (
<React.Suspense fallback={<DashboardSkeleton />}>
<UserProfile />
<Analytics />
<RecentActivity />
</React.Suspense>
);
}
// Nested Suspense for different granularity
function App() {
return (
<React.Suspense fallback={<AppLoader />}>
<Header />
<React.Suspense fallback={<ContentLoader />}>
<MainContent />
</React.Suspense>
<Footer />
</React.Suspense>
);
}
import { BrowserRouter, Routes, Route } from 'react-router-dom';
// Lazy load pages
const Home = React.lazy(() => import('./pages/Home'));
const About = React.lazy(() => import('./pages/About'));
const Dashboard = React.lazy(() => import('./pages/Dashboard'));
const Profile = React.lazy(() => import('./pages/Profile'));
function App() {
return (
<BrowserRouter>
<React.Suspense fallback={<PageLoader />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile/:id" element={<Profile />} />
</Routes>
</React.Suspense>
</BrowserRouter>
);
}
const AdminPanel = React.lazy(() => import('./AdminPanel'));
function App() {
const [user, setUser] = useState(null);
return (
<div>
{user?.isAdmin && (
<React.Suspense fallback={<div>Loading panel...</div>}>
<AdminPanel user={user} />
</React.Suspense>
)}
</div>
);
}
// Component with heavy charting library
const ChartComponent = React.lazy(() => import('./ChartComponent'));
function Analytics() {
const [showChart, setShowChart] = useState(false);
return (
<div>
<button onClick={() => setShowChart(true)}>
Show Chart
</button>
{showChart && (
<React.Suspense fallback={<ChartSkeleton />}>
<ChartComponent data={analyticsData} />
</React.Suspense>
)}
</div>
);
}
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('Component loading error:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <div>Failed to load component</div>;
}
return this.props.children;
}
}
// Usage
function App() {
return (
<ErrorBoundary>
<React.Suspense fallback={<Loader />}>
<LazyComponent />
</React.Suspense>
</ErrorBoundary>
);
}
function LazyComponentWithRetry(componentPath) {
return React.lazy(() =>
import(componentPath).catch(() => {
// Reload page on chunk loading error
window.location.reload();
// Or retry
return new Promise((resolve) => {
setTimeout(() => {
resolve(import(componentPath));
}, 1500);
});
})
);
}
const MyComponent = LazyComponentWithRetry('./MyComponent');
// Preload function
const preloadComponent = (component) => {
component._init();
};
// Create lazy component
const HeavyModal = React.lazy(() => import('./HeavyModal'));
// Preload on hover
function App() {
const [showModal, setShowModal] = useState(false);
return (
<div>
<button
onMouseEnter={() => preloadComponent(HeavyModal)}
onClick={() => setShowModal(true)}
>
Open Modal
</button>
{showModal && (
<React.Suspense fallback={<ModalLoader />}>
<HeavyModal onClose={() => setShowModal(false)} />
</React.Suspense>
)}
</div>
);
}
// Component with progress bar
function ProgressiveSuspense({ children }) {
const [progress, setProgress] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setProgress(prev => Math.min(prev + 10, 90));
}, 100);
return () => clearInterval(timer);
}, []);
return (
<React.Suspense
fallback={
<div>
<div>Loading... {progress}%</div>
<ProgressBar value={progress} />
</div>
}
>
{children}
</React.Suspense>
);
}
// Function for dynamic locale loading
const loadLocale = (locale) => {
return React.lazy(() =>
import(`./locales/${locale}.js`).then(module => ({
default: module.LocaleProvider
}))
);
};
function App() {
const [locale, setLocale] = useState('en');
const LocaleProvider = useMemo(() => loadLocale(locale), [locale]);
return (
<React.Suspense fallback={<div>Loading language...</div>}>
<LocaleProvider>
<MainApp />
</LocaleProvider>
</React.Suspense>
);
}
// Instead of many small lazy imports
const Button = React.lazy(() => import('./Button'));
const Input = React.lazy(() => import('./Input'));
const Select = React.lazy(() => import('./Select'));
// Better to group in one module
const FormComponents = React.lazy(() => import('./FormComponents'));
// FormComponents exports { Button, Input, Select }
// By functionality
const AdminFeatures = React.lazy(() => import('./features/admin'));
const UserFeatures = React.lazy(() => import('./features/user'));
// By bundle size
const LargeTable = React.lazy(() =>
import(/* webpackChunkName: "large-table" */ './LargeTable')
);
// By loading priority
const CriticalComponent = React.lazy(() =>
import(/* webpackPreload: true */ './CriticalComponent')
);
const OptionalComponent = React.lazy(() =>
import(/* webpackPrefetch: true */ './OptionalComponent')
);
// ❌ Doesn't work directly with named exports
const { Component } = React.lazy(() => import('./module'));
// ✅ Need to transform
const Component = React.lazy(() =>
import('./module').then(module => ({
default: module.Component
}))
);
// For SSR use loadable-components or similar solutions
import loadable from '@loadable/component';
const LazyComponent = loadable(() => import('./Component'));
// ❌ Forgot Suspense = error
<LazyComponent />
// ✅ Always wrap in Suspense
<React.Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</React.Suspense>
React.lazy() — is a powerful tool for optimizing React application performance that, when used correctly, significantly improves initial loading time.
Want more interview prep articles? Subscribe to EasyAdvice, bookmark the site, and improve every day 💪