Introducción/Guías/Carga Diferida

Cómo cargar de forma diferida Componentes de Cliente y bibliotecas

La carga diferida (Lazy loading) 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 de 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 de Servidor se dividen automáticamente en código (code splitting), y puedes usar streaming para enviar progresivamente partes de la interfaz de usuario desde el servidor al cliente. La carga diferida aplica a Componentes de 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

Importando Componentes de Cliente

app/page.js
'use client'

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

// Componentes de 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 inmediatamente, pero en un paquete de cliente separado */}
      <ComponentA />

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

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

Nota: Cuando un Componente de Servidor importa dinámicamente un Componente de Cliente, actualmente no se admite la división automática de código (code splitting).

Omitiendo SSR

Al usar React.lazy() y Suspense, los Componentes de Cliente serán prerenderizados (SSR) por defecto.

Nota: La opción ssr: false solo funcionará para Componentes de Cliente, muévela a Componentes de Cliente para asegurar que la división de código del cliente funcione correctamente.

Si deseas desactivar el prerenderizado para un Componente de Cliente, puedes usar la opción ssr establecida en false:

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

Importando Componentes de Servidor

Si importas dinámicamente un Componente de Servidor, solo los Componentes de Cliente que son hijos del Componente de Servidor se cargarán de forma diferida, no el Componente de Servidor en sí. También ayudará a precargar los recursos estáticos como CSS cuando lo uses en Componentes de Servidor.

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

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

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

Nota: La opción ssr: false no es compatible en Componentes de Servidor. Verás un error si intentas usarla en Componentes de Servidor. ssr: false no está permitido con next/dynamic en Componentes de Servidor. Por favor, muévelo a un Componente de Cliente.

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

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

Añadiendo un componente de carga personalizado

app/page.js
'use client'

import dynamic from 'next/dynamic'

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

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

Importando 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>Hello!</p>
}
app/page.js
import dynamic from 'next/dynamic'

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

On this page