Lazy Loading Client Components and Libraries in Next.js – Optimize Performance with Dynamic Imports and Suspense

Lazy loading in Next.js improves performance by deferring the loading of Client Components and external libraries until they’re needed. This article explains how to use next/dynamic and React.lazy, manage SSR behavior, load third-party libraries on demand, and customize loading states.

Lazy LoadingClient Componentnext/dynamicSuspense

~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