Streaming
En el capítulo anterior, aprendiste sobre los diferentes métodos de renderizado de Next.js. También discutimos cómo las solicitudes de datos lentas pueden afectar el rendimiento de tu aplicación. Veamos cómo puedes mejorar la experiencia del usuario cuando hay solicitudes de datos lentas.
¿Qué es el streaming?
El streaming es una técnica de transferencia de datos que te permite dividir una ruta en "fragmentos" más pequeños y transmitirlos progresivamente desde el servidor al cliente a medida que estén listos.

Con el streaming, puedes evitar que las solicitudes de datos lentas bloqueen toda tu página. Esto permite al usuario ver e interactuar con partes de la página sin tener que esperar a que todos los datos se carguen antes de que se muestre cualquier interfaz de usuario.

El streaming funciona bien con el modelo de componentes de React, ya que cada componente puede considerarse un fragmento.
Hay dos formas de implementar el streaming en Next.js:
- A nivel de página, con el archivo
loading.tsx
(que crea<Suspense>
por ti). - A nivel de componente, con
<Suspense>
para un control más granular.
Veamos cómo funciona esto.
Transmitiendo una página completa con loading.tsx
En la carpeta /app/dashboard
, crea un nuevo archivo llamado loading.tsx
:
Actualiza http://localhost:3000/dashboard, y ahora deberías ver:

Aquí están sucediendo algunas cosas:
loading.tsx
es un archivo especial de Next.js construido sobre React Suspense. Te permite crear una interfaz de usuario de respaldo para mostrar como reemplazo mientras se carga el contenido de la página.- Como
<SideNav>
es estático, se muestra inmediatamente. El usuario puede interactuar con<SideNav>
mientras se carga el contenido dinámico. - El usuario no tiene que esperar a que la página termine de cargarse antes de navegar fuera de ella (esto se llama navegación interrumpible).
¡Felicidades! Acabas de implementar el streaming. Pero podemos hacer más para mejorar la experiencia del usuario. Mostremos un esqueleto de carga en lugar del texto Cargando…
.
Agregando esqueletos de carga
Un esqueleto de carga es una versión simplificada de la interfaz de usuario. Muchos sitios web los usan como marcador de posición (o respaldo) para indicar a los usuarios que el contenido se está cargando. Cualquier interfaz de usuario que agregues en loading.tsx
se incrustará como parte del archivo estático y se enviará primero. Luego, el resto del contenido dinámico se transmitirá desde el servidor al cliente.
Dentro de tu archivo loading.tsx
, importa un nuevo componente llamado <DashboardSkeleton>
:
Luego, actualiza http://localhost:3000/dashboard, y ahora deberías ver:

Solucionando el error del esqueleto de carga con grupos de rutas
En este momento, tu esqueleto de carga se aplicará a las facturas.
Como loading.tsx
está un nivel más alto que /invoices/page.tsx
y /customers/page.tsx
en el sistema de archivos, también se aplica a esas páginas.
Podemos cambiar esto con Grupos de Rutas. Crea una nueva carpeta llamada /(overview)
dentro de la carpeta dashboard. Luego, mueve tus archivos loading.tsx
y page.tsx
dentro de la carpeta:

Ahora, el archivo loading.tsx
solo se aplicará a tu página de resumen del dashboard.
Los grupos de rutas te permiten organizar archivos en grupos lógicos sin afectar la estructura de la ruta URL. Cuando creas una nueva carpeta usando paréntesis ()
, el nombre no se incluirá en la ruta URL. Entonces /dashboard/(overview)/page.tsx
se convierte en /dashboard
.
Aquí, estás usando un grupo de rutas para asegurarte de que loading.tsx
solo se aplique a tu página de resumen del dashboard. Sin embargo, también puedes usar grupos de rutas para separar tu aplicación en secciones (por ejemplo, rutas (marketing)
y rutas (shop)
) o por equipos para aplicaciones más grandes.
Transmitiendo un componente
Hasta ahora, estás transmitiendo una página completa. Pero también puedes ser más granular y transmitir componentes específicos usando React Suspense.
Suspense te permite diferir el renderizado de partes de tu aplicación hasta que se cumpla alguna condición (por ejemplo, se carguen los datos). Puedes envolver tus componentes dinámicos en Suspense. Luego, pasarle un componente de respaldo para mostrar mientras se carga el componente dinámico.
Si recuerdas la solicitud de datos lenta, fetchRevenue()
, esta es la solicitud que está ralentizando toda la página. En lugar de bloquear toda tu página, puedes usar Suspense para transmitir solo este componente y mostrar inmediatamente el resto de la interfaz de usuario de la página.
Para hacerlo, necesitarás mover la obtención de datos al componente, actualicemos el código para ver cómo se vería:
Elimina todas las instancias de fetchRevenue()
y sus datos de /dashboard/(overview)/page.tsx
:
Luego, importa <Suspense>
desde React, y envuélvelo alrededor de <RevenueChart />
. Puedes pasarle un componente de respaldo llamado <RevenueChartSkeleton>
.
Finalmente, actualiza el componente <RevenueChart>
para que obtenga sus propios datos y elimina la propiedad que se le pasa:
Ahora actualiza la página, deberías ver la información del dashboard casi inmediatamente, mientras se muestra un esqueleto de respaldo para <RevenueChart>
:

Práctica: Transmitiendo <LatestInvoices>
¡Ahora es tu turno! Practica lo que acabas de aprender transmitiendo el componente <LatestInvoices>
.
Mueve fetchLatestInvoices()
desde la página al componente <LatestInvoices>
. Envuelve el componente en un límite <Suspense>
con un respaldo llamado <LatestInvoicesSkeleton>
.
Una vez que estés listo, expande el toggle para ver el código de solución:
Agrupando componentes
¡Genial! Ya casi estás ahí, ahora necesitas envolver los componentes <Card>
en Suspense. Podrías obtener datos para cada tarjeta individual, pero esto podría provocar un efecto de aparición a medida que las tarjetas se cargan, lo que puede ser visualmente molesto para el usuario.
Entonces, ¿cómo abordarías este problema?
Para crear más un efecto escalonado, puedes agrupar las tarjetas usando un componente envoltorio. Esto significa que el <SideNav/>
estático se mostrará primero, seguido por las tarjetas, etc.
En tu archivo page.tsx
:
- Elimina tus componentes
<Card>
. - Elimina la función
fetchCardData()
. - Importa un nuevo componente envoltorio llamado
<CardWrapper />
. - Importa un nuevo componente esqueleto llamado
<CardsSkeleton />
. - Envuelve
<CardWrapper />
en Suspense.
Luego, ve al archivo /app/ui/dashboard/cards.tsx
, importa la función fetchCardData()
, e invócala dentro del componente <CardWrapper/>
. Asegúrate de descomentar cualquier código necesario en este componente.
Actualiza la página, y deberías ver todas las tarjetas cargarse al mismo tiempo. Puedes usar este patrón cuando quieras que varios componentes se carguen al mismo tiempo.
Decidiendo dónde colocar tus límites de Suspense
Dónde coloques tus límites de Suspense dependerá de algunas cosas:
- Cómo quieres que el usuario experimente la página mientras se transmite.
- Qué contenido quieres priorizar.
- Si los componentes dependen de la obtención de datos.
Echa un vistazo a tu página del dashboard, ¿hay algo que hubieras hecho de manera diferente?
No te preocupes. No hay una respuesta correcta.
- Podrías transmitir toda la página como lo hicimos con
loading.tsx
... pero eso puede llevar a un tiempo de carga más largo si uno de los componentes tiene una obtención de datos lenta. - Podrías transmitir cada componente individualmente... pero eso puede provocar que la interfaz de usuario aparezca en la pantalla a medida que esté lista.
- También podrías crear un efecto escalonado transmitiendo secciones de la página. Pero necesitarás crear componentes envoltorios.
Dónde coloques tus límites de Suspense variará según tu aplicación. En general, es una buena práctica mover tus obtenciones de datos a los componentes que las necesitan, y luego envolver esos componentes en Suspense. Pero no hay nada malo en transmitir las secciones o toda la página si eso es lo que necesita tu aplicación.
No tengas miedo de experimentar con Suspense y ver qué funciona mejor, es una API poderosa que puede ayudarte a crear experiencias de usuario más agradables.
Mirando hacia adelante
El streaming y los Componentes del Servidor nos dan nuevas formas de manejar la obtención de datos y los estados de carga, con el objetivo final de mejorar la experiencia del usuario final.
En el próximo capítulo, aprenderás sobre el Prerrenderizado Parcial, un nuevo modelo de renderizado de Next.js construido con el streaming en mente.