Use React Suspense and lazy loading to split your JavaScript bundle and improve initial load times in React and Next.js applications.

Abdur Razzak
Full-Stack Web Developer
A common performance issue in React applications is a large JavaScript bundle that must be downloaded and parsed before users see anything. Every component, library, and utility you import ends up in this bundle. Code splitting solves this by breaking the bundle into smaller chunks that load on demand. React.lazy and Suspense are the built-in mechanisms for component-level code splitting.
Wrap a dynamic import in React.lazy() to create a lazily loaded component: const HeavyComponent = React.lazy(() => import('./HeavyComponent')). Wrap it in a Suspense boundary with a fallback prop to show while the component chunk loads. The fallback can be a spinner, skeleton, or any React element. If loading fails, wrap in an ErrorBoundary to handle the error gracefully.
The most impactful code splitting point is at the route level — each page gets its own chunk. In React Router v6, use lazy() and a loader/action pattern for route-level splitting. This means users only download the JavaScript for pages they actually visit. A typical single-page app can reduce initial bundle size by 40-60% with route-level splitting alone.
Next.js provides next/dynamic as a wrapper around React.lazy with SSR support. Use dynamic(() => import('./HeavyComponent'), { ssr: false }) to skip server-side rendering for browser-only components (like chart libraries or rich text editors). Use the loading option to provide a fallback. Next.js automatically handles the chunk naming and loading for you.
Preloading allows you to start downloading a component before the user navigates to it. For route prefetching, Next.js Link component preloads linked pages automatically when they enter the viewport. For component preloading, call the import() function (without awaiting it) on hover of a button that will trigger the lazy component. This eliminates perceived loading time for likely next actions.
Use next build and the @next/bundle-analyzer package to visualize your bundle composition as an interactive treemap. Identify the largest chunks and libraries. Common wins: dynamically import charting libraries (Recharts, Chart.js), rich text editors (TipTap, Quill), and date picker components. Track your bundle size over time in CI to prevent regressions.