Menu

Change language

GitHub repo

Skeleton

skeletonui

Animated placeholder rectangles for loading states — drop one in shape of the content that’s about to render. Variants for text lines, avatars, rounded rectangles, and circles.

Installation

$npx shadcn-svelte@latest add https://uipkge.dev/r/svelte/skeleton.json
Manifest:https://uipkge.dev/r/svelte/skeleton.jsonInstalls to:src/lib/components/ui/skeleton/

Examples

Loading interactive previews…

Props

NameType / ValuesDefaultRequired
variant
'text''chip''chip-icon''article''avatar''avatar-small''avatar-large''heading''heading-medium''heading-small''image''image-small''image-large''card''card-avatar''actions''table''table-row''button''button-icon''badge''tab''date-picker''list-item''list-item-two-line''list-item-three-line'
textoptional
widthstring—optional
heightstring—optional
loadingbooleantrueoptional
refHTMLDivElement | null—optional

npm dependencies

Used by

Files installed (6)

  • src/lib/components/ui/skeleton/Skeleton.svelte2.2 kB
    <script lang="ts" module>
      import type { HTMLAttributes } from 'svelte/elements'
    
      export interface SkeletonProps extends HTMLAttributes<HTMLDivElement> {
        variant?: 'rectangular' | 'rounded' | 'circular' | 'text' | 'avatar' | 'image' | 'card' | 'table-row'
        width?: string
        height?: string
        loading?: boolean
        ref?: HTMLDivElement | null
      }
    </script>
    
    <script lang="ts">
      import { cn } from '$lib/utils'
    
      const variantClasses: Record<NonNullable<SkeletonProps['variant']>, string> = {
        rectangular: '',
        rounded: 'rounded-md',
        circular: 'rounded-full',
        text: 'rounded h-4 w-full',
        avatar: 'rounded-full size-10',
        image: 'rounded-lg size-24',
        card: 'rounded-xl size-full min-h-30',
        'table-row': 'rounded h-10 w-full',
      }
    
      let {
        class: className,
        variant = 'rectangular',
        width,
        height,
        loading = true,
        children,
        ref = $bindable(null),
        ...restProps
      }: SkeletonProps = $props()
    
      const variantStyle = $derived(
        width || height ? `${width ? `width:${width};` : ''}${height ? `height:${height};` : ''}` : undefined,
      )
    </script>
    
    {#if loading}
      <div
        bind:this={ref}
        data-uipkge=""
        data-slot="skeleton"
        aria-hidden="true"
        class={cn('skeleton-shimmer', variantClasses[variant ?? 'rectangular'], className)}
        style={variantStyle}
        {...restProps}
      ></div>
    {:else}
      {@render children?.()}
    {/if}
    
    <style>
      /* Slow left-to-right gradient sweep. Per NN/g, slow shimmer reads as
         faster loading than a pulse. 1.8s loop is the documented sweet spot. */
      .skeleton-shimmer {
        background: linear-gradient(
          90deg,
          color-mix(in srgb, var(--muted) 100%, transparent) 0%,
          color-mix(in srgb, var(--muted) 60%, var(--foreground) 8%) 50%,
          color-mix(in srgb, var(--muted) 100%, transparent) 100%
        );
        background-size: 200% 100%;
        animation: skeleton-shimmer 1.8s linear infinite;
      }
      @keyframes skeleton-shimmer {
        from {
          background-position: 200% 0;
        }
        to {
          background-position: -200% 0;
        }
      }
      @media (prefers-reduced-motion: reduce) {
        .skeleton-shimmer {
          animation: none;
          background: var(--muted);
        }
      }
    </style>
    
  • src/lib/components/ui/skeleton/SkeletonGroup.svelte0.5 kB
  • src/lib/components/ui/skeleton/SkeletonLoader.svelte4.2 kB
  • src/lib/components/ui/skeleton/SkeletonText.svelte1.7 kB
  • src/lib/components/ui/skeleton/skeleton.variants.ts1.6 kB
  • src/lib/components/ui/skeleton/index.ts0.6 kB

Raw manifest:https://uipkge.dev/r/svelte/skeleton.json