Lazy loading in Next.js helps reduce the initial JavaScript bundle by deferring the load of components and libraries until they’re needed.
There are two main approaches:
dynamic() from next/dynamic — the primary API for lazy loading in the App Router.
React.lazy() with Suspense — works in Client Components.
dynamic()
next/dynamic is a combination of React.lazy() and Suspense. It works in both Server and Client Components.
Basic usage
import dynamic from 'next/dynamic'
const DynamicComponent = dynamic(() => import('./heavy-component'))
export default function Page() {
return (
<div>
<DynamicComponent />
</div>
)
}
Loading UI
Show a fallback while the component loads:
import dynamic from 'next/dynamic'
const DynamicComponent = dynamic(
() => import('./heavy-component'),
{
loading: () => <p>Loading...</p>,
}
)
export default function Page() {
return <DynamicComponent />
}
Disabling SSR
To prevent a component from rendering on the server, use ssr: false. This is useful for components that depend on browser APIs:
'use client'
import dynamic from 'next/dynamic'
const DynamicComponent = dynamic(
() => import('./browser-only-component'),
{ ssr: false }
)
export default function Page() {
return <DynamicComponent />
}
Using ssr: false prevents any server-rendered HTML for that component. Use only when necessary, such as for components that access window or other browser-only globals.
Named exports
To lazy load a named export from a module, return it from the Promise returned by the dynamic import:
import dynamic from 'next/dynamic'
const DynamicChart = dynamic(() =>
import('./charts').then((mod) => mod.BarChart)
)
export default function Page() {
return <DynamicChart />
}
Lazy loading libraries
Third-party libraries can also be loaded on demand using import() inside an event handler:
'use client'
import { useState } from 'react'
export default function Page() {
const [results, setResults] = useState(null)
const handleSearch = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const { default: Fuse } = await import('fuse.js')
const fuse = new Fuse(['item1', 'item2', 'item3'])
const result = fuse.search('item')
setResults(result)
}
return (
<form onSubmit={handleSearch}>
<button type="submit">Search</button>
{results && <pre>{JSON.stringify(results, null, 2)}</pre>}
</form>
)
}
React.lazy() with Suspense
Inside Client Components, you can use React.lazy() and Suspense directly:
'use client'
import { lazy, Suspense } from 'react'
const DynamicComponent = lazy(() => import('./heavy-component'))
export default function Page() {
return (
<Suspense fallback={<p>Loading...</p>}>
<DynamicComponent />
</Suspense>
)
}
dynamic() from next/dynamic supports additional options like ssr: false that React.lazy() does not. Prefer dynamic() for full Next.js feature support.
dynamic() options reference
A function that returns a Promise resolving to the component module. Usually an import() call.dynamic(() => import('./my-component'))
A component to render while the dynamic component is loading.{ loading: () => <Skeleton /> }
Whether to server-side render the component. Set to false to skip SSR for browser-only components.