Philosophy

Users feel the care.

Every loading state is a moment of trust. What fills that gap tells users whether you thought about them.

Loading...

Prehistoric

Loading text

No shape, no context. The user is left in the dark.

Past

Spinner

At least it moves. But what exactly is coming next?

Present

Generic bars

Most libraries stop here. Drifts when your UI evolves.

Next

skelter

The loading experience your users deserve.

The best products treat every state as intentional.

Not just the happy path. Not just the hero screen. The loading moment is your UI's first visible frame: make it accurate.

Live demo

See it in action

exit

Real components: skelter measures their layout and generates bones automatically.

Showcase

Get started in minutes

A full demo app · 8 cards, 7 animations, real-time open source data. Zero skeletons written by hand. Each card exposes its source in a built-in VS Code mini-editor.

skelter.dev/demo
Weatherwave
Open-Meteo
Exchange Ratepulse
Frankfurter
Air Qualityshiver
Open-Meteo AQI
Public Holidaysshatter
Nager.Date
HN Top Storyslide
HN Firebase
Health Profilebeat
randomuser.me
Productdrip
DummyJSON
Configurable delay in real time
📊Load time shown in ms per card
🖥Source code in a mini VS Code
🌐Open data, zero API key
View the demo →
Mobile

Try it on your phone

The React Native demo is live. Scan the QR code with Expo Go to see the skeletons running natively on your device.

iOS · App StoreAndroid · Google Play
QR code Expo Go

Scan avec Expo Go

The difference

Every other library
// Other libs: you write the component...
function ArticleCard({ article }) {
return (
<View>
<Image source={{ uri: article.cover }}
style={{ height: 160 }} />
<Text style={{ fontSize: 'var(--fs-lg)' }}>{article.title}</Text>
<Text style={{ color: '#888' }}>{article.excerpt}</Text>
</View>
);
}
// ...then write it AGAIN as a skeleton.
const ArticleCardSkeleton = () => (
<SkeletonPlaceholder>
<View style={{ height: 160 }} />
<View style={{ width: '80%', height: 18 }} />
<View style={{ width: '60%', height: 14 }} />
</SkeletonPlaceholder>
);
// Two components. Always drifting out of sync.
skelter: React Native
// skelter (React Native): write it once.
import { withSkeleton } from 'react-zero-skeleton';
function ArticleCard({ article }) {
return (
<View>
<Image source={{ uri: article.cover }}
style={{ height: 160 }} />
<Text style={{ fontSize: 'var(--fs-lg)' }}>{article.title}</Text>
<Text style={{ color: '#888' }}>{article.excerpt}</Text>
</View>
);
}
export default withSkeleton(ArticleCard);
// Use it:
<ArticleCard hasSkeleton isLoading={isLoading}
article={data} />
// skelter measures the layout.
// Bones generated automatically. Always in sync.

Same import react-zero-skeleton: bundler picks the right version automatically.