FeedbackAvailable

Skeleton

Outlined loading placeholders composed into a full profile card, with an optional pulse and reduced-motion support.

Loading example…

Installation

Add the component and its dependencies from the registry running on your computer.

terminal
npx shadcn@latest add https://brutallab.facilito.dev/r/skeleton.json

Usage

Import the component into your project and keep the source code under your control.

tsx
'use client';

import { useState } from 'react';
import { Skeleton } from '@/components/ui/skeleton';
import { Button } from '@/components/ui/button';
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
import { Card } from '@/components/ui/card';

export default function Demo() {
  const [loading, setLoading] = useState(true);
  const [following, setFollowing] = useState(false);
  return (
    <div className="grid w-full min-w-0 max-w-lg gap-6">
      <Card aria-busy={loading} className="min-h-72 gap-5 p-5">
        {loading ? <>
          <div className="flex items-center gap-4"><Skeleton className="size-10 shrink-0 rounded-full" /><div className="grid flex-1 gap-2"><Skeleton className="h-4 w-3/4" /><Skeleton className="h-3 w-1/2" /></div></div>
          <div className="grid gap-2"><Skeleton className="h-3 w-full" /><Skeleton className="h-3 w-5/6" /><Skeleton className="h-3 w-2/3" /></div>
          <Skeleton className="mt-auto h-11 w-32" />
        </> : <>
          <div className="flex items-center gap-4"><Avatar tone="purple"><AvatarFallback>AN</AvatarFallback></Avatar><div><strong className="font-medium">Ana Rivera</strong><p className="bl-type-small text-muted-foreground">Product designer · Study Club</p></div></div>
          <p className="bl-type-small">Interfaces con carácter, pequeñas ideas y espacio para aprender. Construyendo una pieza a la vez.</p>
          <Button variant="accent" className="mt-auto w-fit" aria-pressed={following} onClick={() => setFollowing(!following)}>{following ? 'Siguiendo / Following' : 'Seguir / Follow'}</Button>
        </>}
      </Card>
      <output className="bl-type-small" aria-live="polite">{loading ? 'Loading profile / Cargando perfil' : 'Profile ready / Perfil listo'}</output>
      <Button variant="outline" className="h-auto min-h-11 w-fit max-w-full whitespace-normal py-2 text-center" onClick={() => setLoading(!loading)}>{loading ? 'Show content / Mostrar contenido' : 'Show loading / Mostrar carga'}</Button>
      <div className="grid gap-3"><p className="bl-type-label">Static / Sin animación</p><Skeleton animated={false} className="h-4 w-full" /><Skeleton animated={false} className="h-4 w-2/3" /></div>
    </div>
  );
}

States & interaction

Appearance, behavior and keyboard checks.

  • Animated or static loading with animated={false}. Respects prefers-reduced-motion.
  • No focus or hover. Shapes are hidden from screen readers; the container announces loading once and reserves final content space.

API Reference

BrutalLab-specific properties. Native attributes are supported as well.

PropTypeDefaultDescription
booleantruePulse while loading. false gives a static placeholder. Reduced motion always disables animation.
string—Set the width, height and shape to reserve the space of the final content.

Accessibility

Rules that should remain intact when adapting the component to a product.

Announce loading once on the container. Do not expose each placeholder as meaningful content.

Retain keyboard focus styles and test the composition with keyboard navigation.

Use className for visual customization; preserve the primitive ARIA attributes.