Skeleton screens and shimmer animations are essential for modern mobile UX. Full-screen spinners are outdated. Users see emptiness and don't know what's loading: a list of 3 items or 300, a small block or a full screen. We implement skeleton screens that show the content structure before it appears. Perceived load speed increases, users stay on screen. Without a skeleton, the app feels sluggish even if data arrives in 500ms. Our experience shows: a properly designed skeleton screen reduces bounce rate on transitions between screens by up to 15%. We have worked on 50+ projects — that's trust in our approach. We guarantee a smooth transition and satisfaction with our work.
In this article we share practical experience implementing skeleton screens on iOS, Android, Flutter, and React Native. We'll cover how to make animations seamless and loading comfortable. If you need consultation or turnkey implementation — contact us, we'll evaluate your project.
Why skeleton is better than spinner
A spinner hides future content, creating uncertainty. A skeleton, on the contrary, shows structure: here will be an image, here text, here a button. Users adapt to the layout and perceive data faster after loading. In tests, we've observed that a skeleton screen reduces perceived latency by 30-50% compared to a classic spinner. Moreover, skeleton does not block interaction — users can start scrolling immediately if the structure is known. Nielsen Norman Group confirms that skeletons improve perceived speed.
How to build skeleton properly
A skeleton is not just gray rectangles. They must exactly replicate the real content structure: width, height, element placement. If a product card takes 120pt height with an 80×80 photo, two lines of text, and a price — the skeleton must have the same height and identical placeholder arrangement.
A common mistake: a skeleton placeholder for text with 100% container width. In reality, text doesn't look like that — it occupies 60–80% width on the first line and 40–50% on the last. Uneven placeholders look more natural.
Recommended sizes for skeleton placeholders:
| Element | Height | Width |
|---|---|---|
| Heading | 16–20 pt | 60–70% of container |
| Text (first line) | 12–14 pt | 85–95% |
| Text (last line) | 12–14 pt | 50–60% |
| Avatar | equal to avatar size | circular shape |
| Image | equal to original aspect ratio | rectangle |
Shimmer animation: how and why
Shimmer is a gradient that moves left to right over skeleton blocks, simulating a "gleam". Without shimmer, skeleton looks static and dead. The animation signals that the system is working, not stuck.
Implementation varies by platform. Here’s a comparison of approaches:
| Platform | Approach | Library |
|---|---|---|
| iOS | CAGradientLayer + CABasicAnimation | SkeletonView |
| Android | Shimmer (Facebook) / PlaceholderHighlight | fblibrary or Compose placeholder |
| React Native | Animated.Value | react-native-skeleton-placeholder |
| Flutter | Shimmer widget | shimmer package |
Example shimmer configuration for iOS
Swift code: ```swift // CAGradientLayer with animation let gradient = CAGradientLayer() gradient.colors = [UIColor.lightGray.cgColor, UIColor.white.cgColor, UIColor.lightGray.cgColor] gradient.locations = [0, 0.5, 1] gradient.startPoint = CGPoint(x: 0, y: 0.5) gradient.endPoint = CGPoint(x: 1, y: 0.5) let animation = CABasicAnimation(keyPath: "locations") animation.fromValue = [-1, -0.5, 0] animation.toValue = [1, 1.5, 2] animation.duration = 1.2 animation.repeatCount = .infinity gradient.add(animation, forKey: "shimmer") ```Shimmer colors: base #E0E0E0 (light mode), highlight #F5F5F5. In dark mode: #2A2A2A base, #3A3A3A highlight. Animation speed: 1–1.5 seconds per full cycle.
How many skeleton screens to show
Mistake: showing only one skeleton for the first row, leaving the rest empty. Correct: show 3–5 card placeholders, mimicking the first visible screen. Users should not see the list ends — until data loads.
Transition from skeleton to real content: fade-in animation 200–300ms. Without animation the transition is abrupt; too long — it's annoying.
How to handle partial loading?
Data often doesn't arrive in one block. For example: post text loads, image doesn't yet. In that case, skeleton remains only for the image — text is already shown. This requires a separate ImagePlaceholder component with shimmer that lives independently from text.
What's included in the skeleton implementation work
- Audit of current screens and creation of a loading state matrix
- Design of skeleton placeholders for each content type (text, image, avatar, custom components)
- Implementation of shimmer animation with dark theme support
- Integration of components with state management (Redux, MobX, BLoC, SwiftUI State)
- Testing on real devices with various network speeds
- Documentation on component usage and parameters
Implementation process: from analysis to deploy
- Analytics: identify all screens that need loading. Record element sizes and positions.
- Design: create skeleton components with variable sizes. Set up animation.
- Implementation: write code for each platform. Use native and cross-platform solutions.
- Testing: test on devices with different network speeds (3G, 4G, Wi-Fi). Ensure smooth transitions.
- Deploy: roll out the update. Monitor metrics (perceived load time, bounce rate).
Timeline — from 1 day for a full set of skeleton states for all key screens. Our pricing starts from €1200 for 5 screens, ensuring a quick ROI. Cost is calculated individually — contact us for an accurate estimate.
Checklist: typical mistakes in skeleton design
- Text placeholder at 100% width — looks unnatural
- No shimmer animation — skeleton looks dead
- Showing only one skeleton block — users see emptiness
- Abrupt transition without fade — creates tactile discomfort
- Ignoring partial loading — content blocks disappear and appear jerkily
If you recognize any of these mistakes — get a consultation. We'll help fix the loading UX in your mobile app. Request a project audit today.
Our skeleton screen design includes shimmer animation for better perceived load speed.







