Saltar al contenido

loading.tsx no es un spinner

Un fichero de carga es una frontera de Suspense alrededor de todo un segmento de ruta, así que decide cuánta parte de tu página espera a tu consulta más lenta. Casi todos están puestos por accidente.

3 min de lectura

Meter un loading.tsx en una ruta te da un estado de carga al instante, y ahí es donde la mayoría de los equipos deja de leer. El fichero no es un componente spinner. Es el fallback de una frontera de Suspense que Next.js pone alrededor del segmento entero, y la frontera es lo que importa.

En la práctica significa: todo lo que hay dentro del segmento espera a lo más lento que haya dentro. Un loading.tsx arriba de una ruta de panel convierte una página con cabecera rápida, barra lateral rápida y un gráfico lento en una página que no muestra nada hasta que el gráfico está listo.

La cabecera y la barra estaban listas en 40 ms. El visitante vio un esqueleto durante dos segundos porque la frontera se dibujó alrededor de las tres cosas.

Dónde va la frontera

Alrededor de lo lento, no alrededor de la página.

// app/dashboard/page.tsx - sin loading.tsx en este directorio.
import { Suspense } from 'react';
 
export default function Dashboard() {
  return (
    <>
      <Header />       {/* se pinta enseguida */}
      <Sidebar />      {/* se pinta enseguida */}
 
      <Suspense fallback={<ChartSkeleton />}>
        <RevenueChart />   {/* lo único que espera */}
      </Suspense>
    </>
  );
}

El armazón llega por streaming primero, el gráfico llega cuando llega, y mientras tanto el visitante tiene algo que leer y algo que pulsar. El mismo tiempo total, una experiencia completamente distinta, y un LCP mucho mejor, porque el elemento pintado más grande ya no es un esqueleto.

Cuándo sí toca un loading a nivel de ruta

No siempre está mal. Úsalo cuando el segmento entero dependa de verdad de una sola petición: una página de artículo que sin el artículo no es página, una vista de detalle que es un registro. Ahí un fallback a nivel de ruta es honesto: es cierto que todavía no hay nada que enseñar.

La prueba es una pregunta: ¿hay algo en esta página que yo pudiera pintar antes de que lleguen los datos? Si la respuesta es sí, la frontera está en el sitio equivocado.

Dos detalles que cuestan una tarde

Un loading.tsx aplica a su segmento y a todos los segmentos por debajo. Un fichero en app/loading.tsx es el fallback de toda la aplicación, que casi nunca es lo que alguien quería decir cuando lo creó trabajando en una ruta.

Y el fallback tiene que reservar el espacio que ocupará su contenido. Un esqueleto de 120 px delante de un gráfico de 400 px es un desplazamiento de 280 px en cuanto llegan los datos, y a CLS le da igual que el salto lo causara tu propio estado de carga.

function ChartSkeleton() {
  // La misma altura que el gráfico, para que nada se mueva al cambiar.
  return <div className="h-[400px] animate-pulse rounded-xl bg-ink-100" />;
}

Eso es todo. loading.tsx es una comodidad para un caso, y <Suspense> es la herramienta para todos los demás, cosa que conviene saber antes de que la comodidad decida por ti.

Volver a todos los artículos