Sebastian Gomez
Carga de Componentes Dinámicos en Next.js
Es posible que, mientras construyes tu aplicación, te encuentres con errores como "document is not defined", "localStorage is not defined" o "window is not defined". Estos problemas ocurren porque hay un componente que no debe ejecutarse en el servidor, sino únicamente en el lado del cliente. Esto suele pasar por las siguientes razones:
- Depende de la API del DOM.
- Depende de datos del lado del cliente.
- Cualquier otra restricción propia del componente.
Primero lo primero en Next.js 16: la directiva "use client"
Antes de saltar a los Dynamic Imports, conviene entender el modelo actual. En el App Router de Next.js 16 (con React 19), todos los componentes son Server Components por defecto: se renderizan en el servidor y no tienen acceso a window, document ni a hooks como useState o useEffect.
Cuando un componente necesita el navegador, lo marcas como Client Component añadiendo la directiva "use client" al principio del archivo. Ese es hoy el mecanismo principal para mover código al cliente:
"use client";
import { useEffect, useState } from "react";
export default function MiComponenteDificil() {
const [ancho, setAncho] = useState(0);
useEffect(() => {
// Aquí sí podemos usar la API del DOM con seguridad.
setAncho(window.innerWidth);
}, []);
return <p>El ancho de la ventana es {ancho}px</p>;
}Con "use client", este componente ya se ejecuta en el navegador. Pero ojo: por defecto Next.js igual lo pre renderiza una vez en el servidor (SSR) para generar el HTML inicial. Si tu componente toca window o document directamente durante el render, es decir, fuera de un useEffect, seguirás viendo el temido "document is not defined". Ahí es donde entran los Dynamic Imports.
¿Cómo utilizar Dynamic Imports en Next.js?
Next.js ofrece una función llamada next/dynamic (Imports Dinámicos) que permite cargar un componente de forma diferida y, opcionalmente, desactivar por completo su renderizado en el servidor con { ssr: false }.
Importante en el App Router (Next.js 16): dynamic(..., { ssr: false }) solo se permite dentro de un Client Component. Si lo usas en un Server Component obtendrás un error de compilación. Por eso el archivo donde lo importas debe empezar con la directiva "use client".
Primero, importa el módulo next/dynamic:
import dynamic from "next/dynamic";Luego, envuelve tu componente con la función dynamic y úsalo con normalidad:
"use client";
// Importa la función 'dynamic' de 'next/dynamic' para cargar componentes de forma diferida.
import dynamic from "next/dynamic";
// Carga 'MiComponenteDificil' dinámicamente y desactiva el render en el servidor (SSR).
const MiComponenteDificil = dynamic(
// Función que retorna una promesa con el componente importado.
() => import("../components/MiComponenteDificil"),
// 'ssr: false' deshabilita el render en el servidor: el componente solo vive en el cliente.
{ ssr: false },
);
// Componente cliente que combina contenido normal con el componente dinámico.
export default function Page() {
return (
<div>
<h1>Esto se renderiza con normalidad</h1>
{/* Esto solo se renderiza en el cliente */}
<MiComponenteDificil />
</div>
);
}Así puedes tener, en una misma página, componentes que sí se renderizan en el servidor y otros que se cargan únicamente en el cliente.
Si tu página principal es un Server Component, lo habitual en el App Router, no pongas el dynamic({ ssr: false }) ahí directamente. En su lugar, extrae la parte cliente a su propio archivo con "use client", como el ejemplo de arriba, e impórtalo desde el Server Component como un componente normal.
Conclusiones
- En Next.js 16, la primera herramienta para código de navegador es la directiva
"use client", que convierte un componente en Client Component. - Los Dynamic Imports con
{ ssr: false }son el complemento para los casos en que un componente nunca debe renderizarse en el servidor, porque tocawindow,documentolocalStoragedurante el render. - Recuerda que
dynamic(..., { ssr: false })solo funciona dentro de un Client Component, así que ese archivo debe llevar"use client". - Puedes combinar componentes de servidor y componentes solo de cliente en una misma página sin problema.
Ejercicios para practicar
- Crea un componente que use la API del DOM, por ejemplo que lea
window.innerWidth, márcalo con"use client"y renderízalo con Dynamic Imports y{ ssr: false }. - Implementa un componente que dependa de datos del lado del cliente, por ejemplo
localStorage, y cárgalo con Dynamic Imports. - Diseña una página que sea un Server Component y que combine contenido renderizado en el servidor con un componente solo de cliente importado dinámicamente.
Resumen
- Errores como "document is not defined" se resuelven asegurando que el componente problemático solo corra en el cliente: empieza por
"use client"y, si hace falta evitar el SSR por completo, usa Dynamic Imports con{ ssr: false }. - La función
dynamicpermite cargar de forma diferida componentes que no deben renderizarse en el servidor, pero en el App Router debe usarse dentro de un Client Component. - Puedes combinar componentes renderizados en el servidor y componentes solo de cliente en una misma página.
Eso es todo, espero que este post te sea de utilidad y lo puedas aplicar a algún proyecto que tengas en mente. Si te sirvió, si quieres añadir alguna opinión o si tienes alguna duda, no dudes en dejarme un comentario aquí abajo. Y recuerda que si te gustó, también puedes compartir este artículo usando los enlaces a las redes sociales en la parte inferior. ¡Buena suerte!
Sebastian Gomez
Creador de contenido principalmente acerca de tecnología.