Chargement différé (Lazy Loading)

Le chargement différé dans Next.js aide à améliorer les performances de chargement initial d'une application en réduisant la quantité de JavaScript nécessaire pour rendre une route.

Il vous permet de différer le chargement des composants clients et des bibliothèques importées, et de ne les inclure dans le bundle client que lorsqu'ils sont nécessaires. Par exemple, vous pourriez vouloir différer le chargement d'une modale jusqu'à ce qu'un utilisateur clique pour l'ouvrir.

Il existe deux façons d'implémenter le chargement différé dans Next.js :

  1. En utilisant les importations dynamiques avec next/dynamic
  2. En utilisant React.lazy() avec Suspense

Par défaut, les composants serveur sont automatiquement découpés en morceaux (code split), et vous pouvez utiliser le streaming pour envoyer progressivement des parties de l'interface du serveur vers le client. Le chargement différé s'applique aux composants clients.

next/dynamic

next/dynamic est une combinaison de React.lazy() et Suspense. Il se comporte de la même manière dans les répertoires app et pages pour permettre une migration progressive.

Exemples

Importation de composants clients

app/page.js
'use client'

import { useState } from 'react'
import dynamic from 'next/dynamic'

// Composants clients :
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>
      {/* Charge immédiatement, mais dans un bundle client séparé */}
      <ComponentA />

      {/* Charge à la demande, uniquement si la condition est remplie */}
      {showMore && <ComponentB />}
      <button onClick={() => setShowMore(!showMore)}>Basculer</button>

      {/* Charge uniquement côté client */}
      <ComponentC />
    </div>
  )
}

Désactivation du SSR

Lorsque vous utilisez React.lazy() et Suspense, les composants clients seront pré-rendus (SSR) par défaut.

Si vous souhaitez désactiver le pré-rendu pour un composant client, vous pouvez utiliser l'option ssr définie sur false :

const ComponentC = dynamic(() => import('../components/C'), { ssr: false })

Importation de composants serveur

Si vous importez dynamiquement un composant serveur, seuls les composants clients enfants du composant serveur seront chargés de manière différée - pas le composant serveur lui-même.

app/page.js
import dynamic from 'next/dynamic'

// Composant serveur :
const ServerComponent = dynamic(() => import('../components/ServerComponent'))

export default function ServerComponentExample() {
  return (
    <div>
      <ServerComponent />
    </div>
  )
}

Chargement de bibliothèques externes

Les bibliothèques externes peuvent être chargées à la demande en utilisant la fonction import(). Cet exemple utilise la bibliothèque externe fuse.js pour la recherche floue. Le module est uniquement chargé côté client après que l'utilisateur a saisi dans le champ de recherche.

app/page.js
'use client'

import { useState } from 'react'

const names = ['Tim', 'Joe', 'Bel', 'Lee']

export default function Page() {
  const [results, setResults] = useState()

  return (
    <div>
      <input
        type="text"
        placeholder="Rechercher"
        onChange={async (e) => {
          const { value } = e.currentTarget
          // Charge dynamiquement fuse.js
          const Fuse = (await import('fuse.js')).default
          const fuse = new Fuse(names)

          setResults(fuse.search(value))
        }}
      />
      <pre>Résultats : {JSON.stringify(results, null, 2)}</pre>
    </div>
  )
}

Ajout d'un composant de chargement personnalisé

app/page.js
import dynamic from 'next/dynamic'

const WithCustomLoading = dynamic(
  () => import('../components/WithCustomLoading'),
  {
    loading: () => <p>Chargement...</p>,
  }
)

export default function Page() {
  return (
    <div>
      {/* Le composant de chargement sera affiché pendant que <WithCustomLoading/> charge */}
      <WithCustomLoading />
    </div>
  )
}

Importation d'exports nommés

Pour importer dynamiquement un export nommé, vous pouvez le retourner depuis la Promise retournée par la fonction import() :

components/hello.js
'use client'

export function Hello() {
  return <p>Bonjour !</p>
}
app/page.js
import dynamic from 'next/dynamic'

const ClientComponent = dynamic(() =>
  import('../components/hello').then((mod) => mod.Hello)
)