
Mira el video si prefieres:
¡Las personas que usan internet son cada vez menos pacientes! Los psicólogos dicen que los usuarios están hambrientos por el contenido, su ventana de atención se redujo de 8 segundos a solo 3 en los últimos años. Ya nadie está dispuesto a esperar, la paciencia es una reliquia obsoleta. 🏺
Por eso, en esta entrada, quiero enseñarte un hack que es más un pattern; un pattern que te permitirá crear “la ilusión de la velocidad” mientras tu sitio web carga y que tus desesperados usuarios no se te vayan. 💨
Primero, los esqueletos
Me gustan los esqueletos, sobre todo el que fuma y que pintó Van Gogh. En una de mis esclavitudes, cuando trabajé en Tripadvisor, escribí un componente skeleton reutilizable (tal vez fue lo más importante que hice en esa chamba). Los skeletons se usan para mantener al usuario informado y manejar sus expectativas, haciéndole “sentir” que la página carga más rápido de lo que en verdad lo hace.
El truco es colocar algunos “mockups” de texto e imágenes u otro tipo de contenido a los que se les conoce como “skeleton screens”. ✅
La hora del código
Para este ejemplo vamos a crear un skeleton supersimple, en TailwindPlay.
<div class="h-2 bg-gray-400 rounded-full w-48 animate-pulse"></div>
Ya está, gracias por leer este post, nos vemos en el siguiente… ok no. 😝 Pero, esto ha sido en extremo fácil ¿cierto? Solo tenemos que apilar estos divs
, cambiarles el ancho y separarlos bonito, meterlos en otro div
contendor y vualá.
<section class="grid animate-pulse gap-2 p-4"> <div class="h-2 w-40 rounded-full bg-gray-400"></div> <div class="h-2 w-48 rounded-full bg-gray-400"></div> <div class="h-2 w-42 rounded-full bg-gray-400"></div> <div class="h-2 w-36 rounded-full bg-gray-400"></div> </section>
Incluso podemos mover la utilidad de la animación (animate-pulse
) al contenedor, pues es lo que realmente nos da un sentido de carga.
Pero, hagamos algo más elaborado
Nuestro skeleton para un pequeño párrafo está listo, pero yo creo que podemos hacerlo un poco mejor, qué tal si añadimos el mockup de una imagen.
<article class="flex animate-pulse gap-2 p-4"> <div class="h-48 w-full rounded-sm bg-gray-400"></div> <section class="flex flex-col gap-2"> <div class="h-3 w-48 my-4 rounded-full bg-gray-400"></div> <div class="h-2 w-40 rounded-full bg-gray-400"></div> <div class="h-2 w-48 rounded-full bg-gray-400"></div> <div class="h-2 w-42 rounded-full bg-gray-400"></div> <div class="h-2 w-36 rounded-full bg-gray-400"></div> </section> </article>
Observa que, hemos añadido un div
extra, un poco más alto, para representar un título. También, agrupamos; para tener el gran cuadro de la izquierda representando una imagen. Solo nos falta añadir un pequeño detalle para que de verdad se sienta como la carga de una imagen. He de tener algún SVG útil, por ahí…
<svg class="w-10 h-10 text-gray-200 dark:text-gray-600" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18"> <path d="M18 0H2a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2Zm-5.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Zm4.376 10.481A1 1 0 0 1 16 15H4a1 1 0 0 1-.895-1.447l3.5-7A1 1 0 0 1 7.468 6a.965.965 0 0 1 .9.5l2.775 4.757 1.546-1.887a1 1 0 0 1 1.618.1l2.541 4a1 1 0 0 1 .028 1.011Z"/> </svg>
Checa qué bien se ve (copia y pega).
<article class="flex animate-pulse gap-2 p-4"> <div class="grid h-48 w-full place-items-center rounded-sm bg-gray-400"> <svg class="h-10 w-10 text-gray-200 dark:text-gray-600" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18"> <path d="M18 0H2a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2Zm-5.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Zm4.376 10.481A1 1 0 0 1 16 15H4a1 1 0 0 1-.895-1.447l3.5-7A1 1 0 0 1 7.468 6a.965.965 0 0 1 .9.5l2.775 4.757 1.546-1.887a1 1 0 0 1 1.618.1l2.541 4a1 1 0 0 1 .028 1.011Z" /> </svg> </div> <section class="flex flex-col gap-2"> <div class="my-4 h-3 w-48 rounded-full bg-gray-400"></div> <div class="h-2 w-40 rounded-full bg-gray-400"></div> <div class="h-2 w-48 rounded-full bg-gray-400"></div> <div class="h-2 w-42 rounded-full bg-gray-400"></div> <div class="h-2 w-36 rounded-full bg-gray-400"></div> <div class="h-2 w-36 rounded-full bg-transparent"></div> <div class="h-2 w-36 rounded-full bg-gray-400"></div> <div class="h-2 w-36 rounded-full bg-gray-400"></div> </section> </article>
Te voy a regalar otro SVG
Ora sí, ya para irnos, te voy a regalar otro SVG, uno de un avatar para que tú mismo puedas construir todos los skeletons que se te ocurran. Y, de paso, vamos a construir otro layout.
<svg class="w-10 h-10 me-3 text-gray-200 dark:text-gray-700" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"> <path d="M10 0a10 10 0 1 0 10 10A10.011 10.011 0 0 0 10 0Zm0 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm0 13a8.949 8.949 0 0 1-4.951-1.488A3.987 3.987 0 0 1 9 13h2a3.987 3.987 0 0 1 3.951 3.512A8.949 8.949 0 0 1 10 18Z"/> </svg>
¡Hey! Se ve re-chulo, ¿apoco no?
<article class="flex max-w-xs animate-pulse flex-col gap-2 p-4 border border-gray-400 m-4 rounded-lg"> <div class="grid h-48 w-full place-items-center rounded-sm bg-gray-400"> <svg class="h-10 w-10 text-gray-100" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18"> <path d="M18 0H2a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2Zm-5.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Zm4.376 10.481A1 1 0 0 1 16 15H4a1 1 0 0 1-.895-1.447l3.5-7A1 1 0 0 1 7.468 6a.965.965 0 0 1 .9.5l2.775 4.757 1.546-1.887a1 1 0 0 1 1.618.1l2.541 4a1 1 0 0 1 .028 1.011Z" /> </svg> </div> <section class="flex flex-col gap-2"> <div class="my-4 h-3 w-48 rounded-full bg-gray-400"></div> <div class="h-2 rounded-full bg-gray-400"></div> <div class="h-2 rounded-full bg-gray-400"></div> <div class="h-2 w-42 rounded-full bg-gray-400"></div> </section> <section class="flex"> <div> <svg class="w-10 h-10 me-3 text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"> <path d="M10 0a10 10 0 1 0 10 10A10.011 10.011 0 0 0 10 0Zm0 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6Zm0 13a8.949 8.949 0 0 1-4.951-1.488A3.987 3.987 0 0 1 9 13h2a3.987 3.987 0 0 1 3.951 3.512A8.949 8.949 0 0 1 10 18Z"/> </svg> </div> <div> <div class="mt-2 h-3 w-20 rounded-full bg-gray-400"></div> <div class="mt-2 h-2 w-32 rounded-full bg-gray-400"></div> </div> </section> </article>
Tienes todo el derecho a pensar que crear skeletons es mucho trabajo, solo para que tu usuario lo vea por medio segundo (o menos, si tu app se preocupa por el First Meaningful Paint), pero tus usuarios lo valen, además, la mitad de ellos, los más impacientes, abandonarán tu sitio web mucho menos que antes. 🤷🏻♀️
Pero nos falta usarlo en una página real
Para completar esta entrada, vamos a llevarnos nuestro maquetado a un componente real y usemos la herramienta que React-Router ha creado para exactamente este caso de uso. 😎
export function HydrateFallback() { return <ProfileSkeleton />; }
Ahora, usemos nuestro componente <ProfileSkeleton />
que hemos inventado especialmente para esta ruta.
¡Genial! 👍
Te dejo el enlace al código de toda la ruta para que lo leas con calma. 😌
export const clientLoader = () => new Promise((res) => setTimeout(res, 3000));
Para que nuestro ejemplo funcione y se pueda ver, fingiremos, por 3 segundos, la carga de datos en el cliente con una función clientLoader
.
Conclusión
Ahora podemos hacer una fiesta de esqueletos, como en una pintura que vi una vez dentro de un castillo... Si quieres saber más sobre este pattern, pues quién mejor para explicartelo que Brendi. Acá está su post.
👀 Es también una buena forma de practicar las posiciones de tus elementos HTML y jugar con grid y flexbox. 🎈
Te dejo el link a los docs de React Router y otros a algunos videos por si aún no lo has usado.
Bueno, ¿qué tal? ¿Ahora también te gustan los esqueletos? 🩻
Abrazo. Bliss.
Enlaces relacionados

¿Será que tú puedes programar?
Checa este otro Post
