Skeleton

Skeleton is a placeholder to show a loading state and the expected shape of a component.

Usage

import { Skeleton } from '@hareui/vue'
<script setup lang="ts">import { Skeleton } from '@hareui/vue'</script><template>  <div class="shadow-panel w-[250px] space-y-5 rounded-lg bg-transparent p-4">    <Skeleton class="h-32 rounded-lg" />    <div class="space-y-3">      <Skeleton class="h-3 w-3/5 rounded-lg" />      <Skeleton class="h-3 w-4/5 rounded-lg" />      <Skeleton class="h-3 w-2/5 rounded-lg" />    </div>  </div></template>

Anatomy

Skeleton is a single element. Size and shape come from class; the default slot is optional.

ui keys: base.

Examples

Text Content

<script setup lang="ts">import { Skeleton } from '@hareui/vue'</script><template>  <div class="w-full max-w-md space-y-3">    <Skeleton class="h-4 w-full rounded" />    <Skeleton class="h-4 w-5/6 rounded" />    <Skeleton class="h-4 w-4/6 rounded" />    <Skeleton class="h-4 w-full rounded" />    <Skeleton class="h-4 w-3/6 rounded" />  </div></template>

User Profile

<script setup lang="ts">import { Skeleton } from '@hareui/vue'</script><template>  <div class="flex items-center gap-3">    <Skeleton class="h-10 w-10 shrink-0 rounded-full" />    <div class="flex-1 space-y-2">      <Skeleton class="h-3 w-36 rounded-lg" />      <Skeleton class="h-3 w-24 rounded-lg" />    </div>  </div></template>

List Items

<script setup lang="ts">import { Skeleton } from '@hareui/vue'</script><template>  <div class="w-full max-w-sm space-y-4">    <div v-for="index in 3" :key="index" class="flex items-center gap-3">      <Skeleton class="h-10 w-10 shrink-0 rounded-lg" />      <div class="flex-1 space-y-2">        <Skeleton class="h-3 w-full rounded" />        <Skeleton class="h-3 w-4/5 rounded" />      </div>    </div>  </div></template>

Grid

<script setup lang="ts">import { Skeleton } from '@hareui/vue'</script><template>  <div class="grid w-full max-w-xl grid-cols-3 gap-4">    <Skeleton class="h-24 rounded-xl" />    <Skeleton class="h-24 rounded-xl" />    <Skeleton class="h-24 rounded-xl" />  </div></template>

Single Shimmer

A synchronized shimmer effect that passes over all skeleton elements at once. Put the container classes below on a parent element (the equivalent of HeroUI's skeleton--shimmer class) and set animation-type="none" on child skeletons.

relative overflow-hidden before:pointer-events-none before:absolute before:inset-0 before:z-10
before:-translate-x-full before:animate-skeleton before:mix-blend-overlay before:content-['']
before:bg-[linear-gradient(90deg,transparent_0%,rgba(255,255,255,0.5)_50%,transparent_100%)]
<script setup lang="ts">import { Skeleton } from '@hareui/vue'</script><template>  <!-- HeroUI's `.skeleton--shimmer` parent rule, written as utilities on the container -->  <div    class="relative grid w-full max-w-xl grid-cols-3 gap-4 overflow-hidden rounded-xl before:pointer-events-none before:absolute before:inset-0 before:z-10 before:-translate-x-full before:animate-skeleton before:bg-[linear-gradient(90deg,transparent_0%,rgba(255,255,255,0.5)_50%,transparent_100%)] before:mix-blend-overlay before:content-['']"  >    <Skeleton animation-type="none" class="h-24 rounded-xl" />    <Skeleton animation-type="none" class="h-24 rounded-xl" />    <Skeleton animation-type="none" class="h-24 rounded-xl" />  </div></template>

Animation Types

Shimmer

Pulse

None

<script setup lang="ts">import { Skeleton } from '@hareui/vue'const types = [  { type: 'shimmer', label: 'Shimmer' },  { type: 'pulse', label: 'Pulse' },  { type: 'none', label: 'None' },] as const</script><template>  <div class="grid w-full max-w-xl grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">    <div v-for="item in types" :key="item.type" class="space-y-2">      <p class="truncate text-xs text-muted">{{ item.label }}</p>      <div class="shadow-panel space-y-3 rounded-lg bg-transparent p-4">        <Skeleton :animation-type="item.type" class="h-20 rounded-lg" />        <Skeleton :animation-type="item.type" class="h-3 w-3/5 rounded-lg" />        <Skeleton :animation-type="item.type" class="h-3 w-4/5 rounded-lg" />      </div>    </div>  </div></template>

Customization

Tailwind CSS

<script setup lang="ts">import { Skeleton } from '@hareui/vue'</script><template>  <div class="w-[250px] space-y-5 rounded-xl border border-border/80 bg-surface p-4 shadow-sm ring-1 ring-black/5 dark:ring-white/10">    <Skeleton class="h-32 animate-shine rounded-lg bg-neutral-200/90 dark:bg-neutral-800/90" />    <div class="space-y-3">      <Skeleton class="h-3 w-3/5 animate-shine rounded-lg bg-neutral-200/90 dark:bg-neutral-800/90" />      <Skeleton class="h-3 w-4/5 animate-shine rounded-lg bg-neutral-200/90 dark:bg-neutral-800/90" />      <Skeleton class="h-3 w-2/5 animate-shine rounded-lg bg-neutral-200/90 dark:bg-neutral-800/90" />    </div>  </div></template>

Global theme override

Override the skeleton theme app-wide with createHareUI:

app.use(createHareUI({
  ui: {
    skeleton: {
      slots: {
        base: 'bg-surface-secondary/50', // Change base background
      },
      variants: {
        animationType: {
          shimmer: { base: 'after:via-surface' }, // Change shimmer gradient color
          pulse: { base: 'animate-pulse opacity-75' }, // Customize pulse animation
          none: { base: 'opacity-50' }, // Style for static skeleton
        },
      },
    },
  },
}))

Global Animation Configuration

You can set a default animation type for all Skeleton components in your application by defining the --skeleton-animation CSS variable:

/* In your global CSS file */
:root {
  /* Possible values: shimmer, pulse, none */
  --skeleton-animation: pulse;
}

/* You can also set different values for light/dark themes */
.light, [data-theme="light"] {
  --skeleton-animation: shimmer;
}

.dark, [data-theme="dark"] {
  --skeleton-animation: pulse;
}

This global setting will be overridden by the animationType prop when specified on individual components. Like HeroUI, the variable is read from :root once per page load, after the component mounts.

Styling Reference

Like HeroUI's, the Skeleton root has no data-slot.

ui keyDescription
baseBase skeleton styles with background and rounded corners

HeroUI's modifier classes correspond to the animationType prop (HareUI renders utilities, not BEM classes, so style through class, ui or createHareUI):

  • .skeleton--shimmer → shimmer (default): shimmer animation with a gradient
  • .skeleton--pulse → pulse: Tailwind's animate-pulse
  • .skeleton--none → none: static skeleton

Animation

Shimmer Animation

The shimmer effect creates a gradient that moves across the skeleton element:

// theme/skeleton.ts, animationType.shimmer
'after:absolute after:inset-0 after:-translate-x-full after:animate-skeleton after:bg-linear-to-r after:from-transparent after:via-surface-tertiary after:to-transparent after:content-[\'\']'

The shimmer animation is defined in the theme using:

@theme inline {
  --animate-skeleton: skeleton 2s linear infinite;

  @keyframes skeleton {
    100% {
      transform: translateX(200%);
    }
  }
}

Pulse Animation

The pulse animation uses Tailwind's built-in animate-pulse utility.

No Animation

animationType="none" applies no animation classes.

API Reference

Props

PropTypeDefaultDescription
asAsTag | Component"div"The element or component to render as.
animationType"none" | "shimmer" | "pulse"-Animation type. When omitted, the --skeleton-animation CSS variable on :root (shimmer, pulse or none) decides, falling back to shimmer.
uiComponentSlots<{ slots: { base: string; }; variants: { animationType: { shimmer: { base: string; }; pulse: { base: string; }; none: { base: string; }; }; }; defaultVariants: { animationType: string; }; }>-Per-slot class overrides.

Slots

SlotPropsDescription
defaultanyOptional content rendered inside the placeholder.

On this page

No Headings