Reduce initial JavaScript
A large widget is not needed immediately on the first view.
Next.js 16+ · Module
next/dynamic lazy-loads Client Components and libraries so they can be omitted from the initial JavaScript bundle. It combines dynamic imports with React lazy loading and Suspense, with options for fallback UI and client-only rendering.
Import dynamic from next/dynamic and call it at module scope with a loader that performs a literal dynamic import. Next.js splits the component from the initial JavaScript and can show a loading component while the import resolves; set ssr: false when the component must only run in the browser.
dynamic(() => import('./Component'), { loading: () => <p>Loading...</p>, ssr: false })| Name | Type | Description |
|---|---|---|
loader | () => import(path) | A function returning a dynamic import() of the component module; the import path must be explicit and the import must be inside dynamic(). |
options | { loading?: () => ReactNode; ssr?: boolean } | Optional configuration including loading for fallback UI and ssr: false to disable server rendering. |
A React component that loads the imported component dynamically.
ssr: false for browser-only dependencies, such as code that requires window.import dynamic from 'next/dynamic'
const ClientOnlyChart = dynamic(() => import('./ClientOnlyChart'), {
ssr: false,
loading: () => <p>Loading chart...</p>,
})
export default function Dashboard() {
return <ClientOnlyChart />
}Loads the chart as a separate client-side component and displays fallback content while it loads.
A large widget is not needed immediately on the first view.
A component depends on browser globals such as window.
A component should appear behind a loading state while its chunk resolves.
next/dynamic solve?next/dynamic combines the behavior of React.lazy() and Suspense. It lets Next.js defer a component's JavaScript so that component is not part of the route's initial bundle.
Follow-up: How would you avoid server-rendering a browser-dependent component? Set ssr: false in the options passed to dynamic.
The loader should be an inline dynamic import with an explicit path, for example dynamic(() => import('./Widget')). Keeping that import inside the call lets Next.js associate the module with the dynamic component and preload it.
Follow-up: Why must the component declaration be outside render? Next.js needs a stable module-level declaration to perform preloading, similarly to React.lazy.
The loading option supplies fallback UI while the dynamically imported component is being resolved. Without it, the component still loads dynamically, but this API call does not provide custom loading content.
Follow-up: What happens when ssr: false is used? The component is skipped during server rendering and loaded in the browser instead.
React.lazy()SuspenseClient ComponentsDynamic Importsloadingssr