~3 min read • Updated Oct 28, 2025
Introduction
Lazy loading in Next.js allows you to defer the loading of Client Components and libraries until they’re actually needed. This reduces the initial JavaScript bundle size and improves page load performance.
Two Approaches
- Using next/dynamic: Combines React.lazy and Suspense with SSR support
- Using React.lazy and Suspense: Works for Client Components only
Example: Lazy Loading Client Components
'use client'
import { useState } from 'react'
import dynamic from 'next/dynamic'
const ComponentA = dynamic(() => import('../components/A'))
const ComponentB = dynamic(() => import('../components/B'))
const ComponentC = dynamic(() => import('../components/C'), { ssr: false })
export default function ClientComponentExample() {
const [showMore, setShowMore] = useState(false)
return (
<div>
<ComponentA />
{showMore && <ComponentB />}
<button onClick={() => setShowMore(!showMore)}>Toggle</button>
<ComponentC />
</div>
)
}Disabling SSR for Client Components
To prevent server-side rendering of a Client Component:
const ComponentC = dynamic(() => import('../components/C'), { ssr: false })Lazy Loading Server Components
When dynamically importing a Server Component, only its Client children are lazy-loaded. The ssr: false option is not supported for Server Components.
const ServerComponent = dynamic(() => import('../components/ServerComponent'))
export default function ServerComponentExample() {
return <ServerComponent />
}Loading External Libraries on Demand
Use import() to load third-party libraries only when needed:
'use client'
import { useState } from 'react'
const names = ['Tim', 'Joe', 'Bel', 'Lee']
export default function Page() {
const [results, setResults] = useState()
return (
<input
type="text"
placeholder="Search"
onChange={async (e) => {
const Fuse = (await import('fuse.js')).default
const fuse = new Fuse(names)
setResults(fuse.search(e.currentTarget.value))
}}
/>
)
}Custom Loading Component
'use client'
import dynamic from 'next/dynamic'
const WithCustomLoading = dynamic(() => import('../components/WithCustomLoading'), {
loading: () => <p>Loading...</p>,
})
export default function Page() {
return <WithCustomLoading />
}Importing Named Exports
To dynamically import a named export:
const ClientComponent = dynamic(() =>
import('../components/hello').then((mod) => mod.Hello)
)Conclusion
Lazy loading in Next.js is a powerful technique for optimizing performance. By deferring the loading of Client Components and libraries, managing SSR behavior, and customizing loading states, you can build faster and more responsive applications.
Written & researched by Dr. Shahin Siami