Carga diferida (Lazy Loading)

La carga diferida en Next.js ayuda a mejorar el rendimiento de carga inicial de una aplicación al reducir la cantidad de JavaScript necesaria para renderizar una ruta.

Te permite diferir la carga de Componentes del Cliente y bibliotecas importadas, incluyéndolas en el paquete del cliente solo cuando son necesarias. Por ejemplo, podrías querer diferir la carga de un modal hasta que un usuario haga clic para abrirlo.

Hay dos formas de implementar la carga diferida en Next.js:

  1. Usando Importaciones dinámicas con next/dynamic
  2. Usando React.lazy() con Suspense

Por defecto, los Componentes del Servidor se dividen en código (code split) automáticamente, y puedes usar streaming para enviar progresivamente partes de la interfaz de usuario desde el servidor al cliente. La carga diferida aplica a Componentes del Cliente.

next/dynamic

next/dynamic es una combinación de React.lazy() y Suspense. Se comporta de la misma manera en los directorios app y pages para permitir una migración incremental.

Ejemplos

Importar Componentes del Cliente

app/page.js
'use client'

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

// Componentes del Cliente:
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>
      {/* Carga inmediata, pero en un paquete separado del cliente */}
      <ComponentA />

      {/* Carga bajo demanda, solo cuando/se cumple la condición */}
      {showMore && <ComponentB />}
      <button onClick={() => setShowMore(!showMore)}>Alternar</button>

      {/* Carga solo en el lado del cliente */}
      <ComponentC />
    </div>
  )
}

Omitir SSR

Cuando se usa React.lazy() y Suspense, los Componentes del Cliente se pre-renderizarán (SSR) por defecto.

Si deseas desactivar el pre-renderizado para un Componente del Cliente, puedes usar la opción ssr establecida en false:

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

Importar Componentes del Servidor

Si importas dinámicamente un Componente del Servidor, solo los Componentes del Cliente que son hijos del Componente del Servidor se cargarán de forma diferida, no el Componente del Servidor en sí.

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

// Componente del Servidor:
const ServerComponent = dynamic(() => import('../components/ServerComponent'))

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

Cargar bibliotecas externas

Las bibliotecas externas se pueden cargar bajo demanda usando la función import(). Este ejemplo usa la biblioteca externa fuse.js para búsqueda difusa. El módulo solo se carga en el cliente después de que el usuario escribe en el campo de búsqueda.

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="Buscar"
        onChange={async (e) => {
          const { value } = e.currentTarget
          // Carga dinámica de fuse.js
          const Fuse = (await import('fuse.js')).default
          const fuse = new Fuse(names)

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

Agregar un componente de carga personalizado

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

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

export default function Page() {
  return (
    <div>
      {/* El componente de carga se renderizará mientras <WithCustomLoading/> está cargando */}
      <WithCustomLoading />
    </div>
  )
}

Importar exportaciones con nombre

Para importar dinámicamente una exportación con nombre, puedes devolverla desde la Promesa retornada por la función import():

components/hello.js
'use client'

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

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