Cómo cargar de forma diferida Componentes de Cliente y bibliotecas
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 de Cliente y bibliotecas importadas, incluyéndolos en el paquete del cliente solo cuando son necesarios. 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:
- Usando Importaciones Dinámicas con
next/dynamic
- Usando
React.lazy()
con Suspense
Por defecto, los Componentes de Servidor se dividen en código (code split) automáticamente, y puedes usar streaming para enviar progresivamente partes de la UI 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
'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)}>Alternar</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 de código (code splitting) automática.
Saltando SSR
Cuando se usa 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 quieres 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.
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 se admite en Componentes de Servidor. Verás un error si intentas usarla en Componentes de Servidor.ssr: false
no está permitido connext/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.
'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ámicamente 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>
)
}
Añadiendo un componente de carga personalizado
'use client'
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>
)
}
Importando Exportaciones con Nombre
Para importar dinámicamente una exportación con nombre, puedes devolverla desde la Promesa retornada por la función import()
:
'use client'
export function Hello() {
return <p>¡Hola!</p>
}
import dynamic from 'next/dynamic'
const ClientComponent = dynamic(() =>
import('../components/hello').then((mod) => mod.Hello)
)