Busca en todas las páginas de la documentación
Una forma de carga de marcador que imita el diseño del contenido antes de que se cargue, reduciendo el tiempo de carga percibido e impidiendo cambios de diseño.
interface SkeletonProps {
className?: string;
}
export function Skeleton({ className }: SkeletonProps) {
return (
<div
className={`animate-pulse rounded-md bg-gray-200 ${className ?? ""}`}
aria-hidden="true"
/>
);
}
// Uso
<Skeleton className="h-4 w-48" />Un único div con el animate-pulse integrado de Tailwind y un fondo neutro. El consumidor controla la forma y el tamaño enteramente a través de className. El atributo aria-hidden oculta el marcador de los lectores de pantalla ya que no tiene contenido significativo.
interface SkeletonTextProps {
lines?: number;
}
export function SkeletonText({ lines = 3 }: SkeletonTextProps) {
return (
<div className="space-y-3" aria-hidden="true">
Genera varias líneas de texto de marcador. La última línea es más corta (w-2/3) para imitar cómo los párrafos reales generalmente terminan a mitad de línea, lo que hace que el esqueleto se sienta más natural.
type AvatarSize = "sm" | "md" | "lg";
interface SkeletonAvatarProps {
size?: AvatarSize;
}
const sizeClasses: Record<AvatarSize, string> = {
sm: "h-8 w-8"
Un esqueleto circular que coincide con las dimensiones comunes del avatar. Usar los mismos valores de tamaño que el componente Avatar real previene cambios de diseño cuando el contenido se carga.
export function SkeletonCard() {
return (
<div className="rounded-xl border border-gray-200 p-4" aria-hidden="true">
<div className="h-40 animate-pulse rounded-lg bg-gray-200" />
<div className="mt-4 space-y-3">
<div
Refleja un diseño de tarjeta típico con imagen, título, descripción y autor. El borde y el relleno coinciden con la tarjeta real para que el esqueleto ocupe exactamente el mismo espacio, eliminando cambios de diseño en la carga.
interface SkeletonTableProps {
rows?: number;
columns?: number;
}
export function SkeletonTable({ rows = 5, columns = 4 }: SkeletonTableProps) {
return (
<
La fila de encabezado usa un matiz ligeramente más oscuro (bg-gray-300) para diferenciarse de las filas de datos. Las columnas flexibles con flex-1 distribuyen el ancho uniformemente para coincidir con un diseño de tabla típico.
interface SkeletonProps {
className?: string;
}
export function Skeleton({ className }: SkeletonProps) {
return (
<div
className={`relative overflow-hidden rounded-md bg-gray-200 ${className ?? ""}`}
Agrega esto a tu tailwind.config.ts para registrar el fotograma clave de shimmer:
// tailwind.config.ts
export default {
theme: {
extend: {
keyframes: {
shimmer: {
"100%": { transform: "translateX(100%)" },
},
},
},
},
};Un efecto de shimmer que barre un degradado claro en el marcador. Esto se siente más pulido que el pulso predeterminado y da a los usuarios una sensación más fuerte de que el contenido se está cargando. El overflow-hidden evita que el degradado se derrame fuera de las esquinas redondeadas.
interface SkeletonProps {
className?: string;
}
function Skeleton({ className }: SkeletonProps) {
return (
<div
className={`animate-pulse rounded-md bg-gray-200 ${className ?? ""}`}
aria-hidden
Compone el primitivo base Skeleton en un diseño específico del perfil. Construir esqueletos específicos del dominio (ProfileSkeleton, CommentSkeleton, etc.) desde un primitivo único mantiene la base de código SECA mientras asegura que cada esqueleto coincida exactamente con su contraparte real.
"use client";
import { useMemo } from "react";
type SkeletonVariant = "text" | "circular" | "rectangular" | "rounded";
interface SkeletonProps {
variant?: SkeletonVariant;
width?: string |
Aspectos clave:
pulse utiliza la animación integrada de Tailwind, shimmer añade un degradado barridor para una sensación premium, y none desactiva la animación para preferencias de movimiento reducido.lines genera varias filas de esqueleto con la última línea más corta, coincidiendo con finales de párrafos naturales.width y height aceptan tanto números (px) como cadenas (porcentajes, rem) a través de estilos en línea, evitando la necesidad de generar clases dinámicas de Tailwind.role="status" con texto "Cargando..." visualmente oculto le dice a los lectores de pantalla que el contenido se está cargando sin mostrar texto en la pantalla.SkeletonCard y SkeletonTable demuestran esqueletos reutilizables y específicos del dominio construidos desde el primitivo base, manteniendo la API tanto flexible como conveniente.Cambio de diseño cuando se carga el contenido -- Si las dimensiones del esqueleto no coinciden con el contenido real, la página salta cuando llegan los datos. Siempre mide y coincide la altura y el ancho del componente real.
Demasiados elementos animate-pulse -- Docenas de elementos pulsantes en una sola página pueden degradar el rendimiento en dispositivos de gama baja. Usa un único animate-pulse padre en el contenedor en lugar de elementos individuales.
Fotograma clave de shimmer no registrado -- La animación de shimmer requiere un fotograma clave personalizado en tailwind.config.ts. Sin él, el elemento de degradado permanece estaticamente traducido fuera de la pantalla.
Falta aria-hidden o role -- Los esqueletos sin aria-hidden="true" o role="status" saturan el árbol de accesibilidad. Los divs vacíos se anuncian como "group" por algunos lectores de pantalla, confundiendo a los usuarios.
El esqueleto nunca desaparece -- Olvidar renderizar condicionalmente el esqueleto frente al contenido real significa que el marcador permanece visible para siempre. Siempre controla la renderización del esqueleto en un estado de carga: {isLoading ? <Skeleton /> : <RealContent />}.
Preferencias de movimiento reducido ignoradas -- Los usuarios con prefers-reduced-motion habilitado pueden encontrar las animaciones continuas distractoras. Respeta esto con motion-safe:animate-pulse o desactiva la animación completamente a través de la propiedad animation="none".