Lazy Loading
Lazy loading in Next.js helps improve the initial loading performance of an application by decreasing the amount of JavaScript needed to render a route.
It allows you to defer loading of Client Components and imported libraries, and only include them in the client bundle when theyโre needed. For example, you might want to defer loading a modal until a user clicks to open it.
There are two ways you can implement lazy loading in Next.js:
- Using Dynamic Imports with
next/dynamic - Using
React.lazy()with Suspense
By default, Server Components are automatically code split, and you can use streaming to progressively send pieces of UI from the server to the client. Lazy loading applies to Client Components.
next/dynamic
next/dynamic is a composite of React.lazy() and Suspense. It behaves the same way in the app and pages directories to allow for incremental migration.
Examples
Importing Client Components
1'use client'23import { useState } from 'react'4import dynamic from 'next/dynamic'56// Client Components:7const ComponentA = dynamic(() => import('../components/A'))8const ComponentB = dynamic(() => import('../components/B'))9const ComponentC = dynamic(() => import('../components/C'), { ssr: false })1011export default function ClientComponentExample() {12const [showMore, setShowMore] = useState(false)1314return (15<div>16{/* Load immediately, but in a separate client bundle */}17<ComponentA />1819{/* Load on demand, only when/if the condition is met */}20{showMore && <ComponentB />}21<button onClick={() => setShowMore(!showMore)}>Toggle</button>2223{/* Load only on the client side */}24<ComponentC />25</div>26)27}
Skipping SSR
When using React.lazy() and Suspense, Client Components will be pre-rendered (SSR) by default.
If you want to disable pre-rendering for a Client Component, you can use the ssr option set to false:
1const ComponentC = dynamic(() => import('../components/C'), { ssr: false })
Importing Server Components
If you dynamically import a Server Component, only the Client Components that are children of the Server Component will be lazy-loaded - not the Server Component itself.
1import dynamic from 'next/dynamic'23// Server Component:4const ServerComponent = dynamic(() => import('../components/ServerComponent'))56export default function ServerComponentExample() {7return (8<div>9<ServerComponent />10</div>11)12}
Loading External Libraries
External libraries can be loaded on demand using the import() function. This example uses the external library fuse.js for fuzzy search. The module is only loaded on the client after the user types in the search input.
1'use client'23import { useState } from 'react'45const names = ['Tim', 'Joe', 'Bel', 'Lee']67export default function Page() {8const [results, setResults] = useState()910return (11<div>12<input13type="text"14placeholder="Search"15onChange={async e => {16const { value } = e.currentTarget17// Dynamically load fuse.js18const Fuse = (await import('fuse.js')).default19const fuse = new Fuse(names)2021setResults(fuse.search(value))22}}23/>24<pre>Results: {JSON.stringify(results, null, 2)}</pre>25</div>26)27}
Adding a custom loading component
1import dynamic from 'next/dynamic'23const WithCustomLoading = dynamic(4() => import('../components/WithCustomLoading'), //5{6loading: () => <p>Loading...</p>,7},8)910export default function Page() {11return (12<div>13{/* The loading component will be rendered while <WithCustomLoading/> is loading */}14<WithCustomLoading />15</div>16)17}
Importing Named Exports
To dynamically import a named export, you can return it from the Promise returned by import() function:
1'use client'23export function Hello() {4return <p>Hello!</p>5}
1import dynamic from 'next/dynamic'23const ClientComponent = dynamic(4() => import('../components/hello').then(mod => mod.Hello), //5)