Advanced Components

Skeleton

Placeholder directives that render an animated shimmer while content is loading, keeping the layout stable and giving users immediate visual feedback.

Example

Apply buiSkeleton to any element to turn it into an animated placeholder. Combine with buiSkeletonRepeat on an ng-template to render multiple rows at once.

Single skeleton
Repeated skeleton
Skeleton with enabled toggled off
<!-- Single skeleton lines -->
<div buiSkeleton class="h-5 w-48"></div>
<div buiSkeleton></div>
<div buiSkeleton class="h-5 w-3/4"></div>

<!-- Repeated skeleton rows -->
<ng-template [buiSkeletonRepeat]="3" let-index="index">
<div class="flex items-center gap-x-3">
<div buiSkeleton class="h-8 w-8 rounded-full"></div>
<div class="flex flex-1 flex-col gap-y-1.5">
<div buiSkeleton class="h-3 w-1/3"></div>
<div buiSkeleton class="h-3 w-2/3"></div>
</div>
</div>
</ng-template>

<!-- enabled=false removes all skeleton styles (element stays in the DOM) -->
<div buiSkeleton [enabled]="false" class="h-5 w-48"></div>

buiSkeleton

An attribute directive that adds the shimmer animation and base skeleton styles to any host element. Unlike buiSkeletonRepeat, it is not a structural directive. The host element and any content inside it are always present in the DOM regardless of the enabled state. enabled only toggles the CSS classes that produce the shimmer effect.

<!-- Default (full width, h-4) -->
<div buiSkeleton></div>

<!-- Custom size -->
<div buiSkeleton class="h-6 w-32"></div>

<!-- Circle avatar placeholder -->
<div buiSkeleton class="size-10 rounded-full"></div>

<!-- enabled toggles shimmer styles only; the element is always in the DOM -->
<div buiSkeleton [enabled]="isLoading()"></div>
PropTypeDefaultDescription
enabledbooleantrue Toggles the skeleton CSS classes on and off. Does not affect DOM presence. The host element and its children are always rendered. Useful as a convenience when you need a directive to programmatically control whether the shimmer is shown.

buiSkeletonRepeat

A structural directive applied to an ng-template that stamps out its content a given number of times. It exposes template context variables so each iteration can be styled differently.

<!-- Stamp out 5 rows -->
<ng-template [buiSkeletonRepeat]="5" let-i>
<div buiSkeleton class="h-4"></div>
</ng-template>

<!-- Use context variables -->
<ng-template
[buiSkeletonRepeat]="4"
let-index="index"
let-first="first"
let-last="last"
>
<div
buiSkeleton
[class]="first ? 'h-6 w-1/2' : 'h-4'"
></div>
</ng-template>
PropTypeDefaultDescription
buiSkeletonRepeatnumber1Number of times to render the template.

Template context

The following variables are available inside the ng-template:

VariableTypeDescription
$implicitnumberZero-based index of the current iteration.
indexnumberZero-based index of the current iteration.
countnumberTotal number of iterations.
firstbooleantrue for the first item.
lastbooleantrue for the last item.
oddbooleantrue for odd-numbered items (1st, 3rd, …).
evenbooleantrue for even-numbered items (2nd, 4th, …).

Compact repeat

When you don't need the template context variables, you can apply *buiSkeletonRepeat directly on the skeleton element itself. Angular's structural directive shorthand desugars it into an ng-template automatically, keeping the template concise.

<!-- No ng-template needed when context variables are not required -->
<div *buiSkeletonRepeat="5" buiSkeleton></div>

<!-- With a custom shape -->
<div *buiSkeletonRepeat="3" buiSkeleton class="h-8 w-8 rounded-full"></div>

Conditional skeleton

Because buiSkeleton only toggles CSS classes, the typical pattern is to use empty placeholder elements alongside an @if that swaps in the real content once it has loaded. Use [enabled] when a directive-level style toggle is more convenient than restructuring the template with an @if:

<!-- Recommended: use @if to swap placeholder elements with real content -->
@if (isLoading()) {
<div buiSkeleton class="h-6 w-40"></div>
<div buiSkeleton class="h-4 w-full"></div>
<div buiSkeleton class="h-4 w-3/4"></div>
} @else {
<h2>{{ article.title }}</h2>
<p>{{ article.body }}</p>
}

<!-- Alternatively, use [enabled] when a style-only toggle is more convenient -->
<div buiSkeleton [enabled]="isLoading()" class="h-6 w-40"></div>

Custom shapes

Use the class input to override the default dimensions and border radius. The default skeleton is h-4 w-full rounded-xl:

<!-- Rectangular image placeholder -->
<div buiSkeleton class="h-48 w-full rounded-lg"></div>

<!-- Avatar circle -->
<div buiSkeleton class="size-12 rounded-full"></div>

<!-- Pill-shaped tag -->
<div buiSkeleton class="h-5 w-16 rounded-full"></div>

<!-- Square thumbnail -->
<div buiSkeleton class="size-16 rounded-md"></div>