Skeleton
Animated loading placeholders that match the shape of content before data arrives. Uses role="status" + .skeleton classes from @primus/ui-core — no JavaScript required.
Code​
- HTML · @primus/ui-core
- React
- Angular
<!-- Text lines -->
<div role="status" class="skeleton line"></div>
<div role="status" class="skeleton line" style="width: 60%"></div>
<div role="status" class="skeleton line-sm"></div>
<!-- Avatar circle -->
<div role="status" class="skeleton avatar"></div>
<div role="status" class="skeleton circle"></div>
<!-- Image/card box -->
<div role="status" class="skeleton box"></div>
<!-- Custom size -->
<div role="status" class="skeleton line" style="height: 2rem; width: 40%; border-radius: 4px"></div>
<!-- Stat card skeleton -->
<div class="card" style="padding:1rem">
<div role="status" class="skeleton line-sm" style="width:50%;margin-bottom:0.5rem"></div>
<div role="status" class="skeleton line" style="height:2rem;width:70%;margin-bottom:0.5rem"></div>
<div role="status" class="skeleton line-sm" style="width:40%"></div>
</div>
// Skeleton is pure HTML — no import needed
// Use conditional rendering: skeleton when loading, real content when ready
function StatCardSkeleton() {
return (
<div className="card" style={{ padding: '1rem' }}>
<div role="status" className="skeleton line-sm"
style={{ width: '50%', marginBottom: 8 }} />
<div role="status" className="skeleton line"
style={{ height: '2rem', width: '70%', marginBottom: 8, borderRadius: 4 }} />
<div role="status" className="skeleton line-sm"
style={{ width: '40%' }} />
</div>
);
}
function DashboardStats({ isLoading, stats }) {
if (isLoading) {
return (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: '0.75rem' }}>
{Array.from({ length: 4 }).map((_, i) => <StatCardSkeleton key={i} />)}
</div>
);
}
return (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: '0.75rem' }}>
{stats.map(s => <PrimusStatCard key={s.id} {...s} />)}
</div>
);
}
<!-- dashboard-stats.component.html -->
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:0.75rem">
<!-- Loading state -->
<ng-container *ngIf="loading">
<div *ngFor="let _ of [1,2,3,4]" class="card" style="padding:1rem">
<div role="status" class="skeleton line-sm" style="width:50%;margin-bottom:0.5rem"></div>
<div role="status" class="skeleton line" style="height:2rem;width:70%;border-radius:4px;margin-bottom:0.5rem"></div>
<div role="status" class="skeleton line-sm" style="width:40%"></div>
</div>
</ng-container>
<!-- Loaded state -->
<primus-stats-card *ngFor="let stat of stats"
[label]="stat.label"
[value]="stat.value"
[delta]="stat.delta"
[trend]="stat.trend">
</primus-stats-card>
</div>
Skeleton classes​
| Class | Shape | Dimensions |
|---|---|---|
.skeleton.line | Full-width line | 100% × 1rem, rounded |
.skeleton.line-sm | Narrow line | 100% × 0.75rem, rounded |
.skeleton.avatar | Circle | 2.5rem × 2.5rem |
.skeleton.circle | Circle (alias) | 2.5rem × 2.5rem |
.skeleton.box | Rectangle | 100% × 8rem |
All classes apply the shimmer animation automatically. Override size with inline styles.
Version history
See the Changelog for version history and breaking changes.