
Skeleton Shimmer
From syxUI — written for both platforms, not translated between them.
Placeholder blocks with a sweeping highlight, sized to the real content.
Loaders · SwiftUI · Flutter · iOS 17 · Flutter 3.27
- loader
- skeleton
- shimmer
- placeholder
- loading
- list
The actual source
SkeletonShimmer.swift
// Skeleton Shimmer · syxUI · https://syxui.dev/components/skeleton-shimmer
// Free in any project. Keep this line and credit syxUI where a person can read it.
import SwiftUI
/// Placeholder blocks with a highlight sweeping across them.
///
/// `SkeletonBlock` is the reusable piece; `SkeletonShimmer` is a ready-made
/// list row built from three of them. Match the block sizes to the real
/// content so the layout does not jump when data arrives.
struct SkeletonShimmer: View {
/// Drive the sweep yourself with 0...1 instead of letting it animate.
var phase: Double? = nil
var body: some View {
HStack(spacing: 14) {
SkeletonBlock(width: 54, height: 54, cornerRadius: 16, phase: phase)
VStack(alignment: .leading, spacing: 8) {
SkeletonBlock(width: 150, height: 13, phase: phase)
SkeletonBlock(width: 210, height: 11, phase: phase)
SkeletonBlock(width: 96, height: 11, phase: phase)
}
Spacer(minLength: 0)
}
.frame(width: 300)
.accessibilityElement()
.accessibilityLabel("Loading content")
}
}
struct SkeletonBlock: View {
var width: CGFloat? = nil
var height: CGFloat = 12
var cornerRadius: CGFloat = 6
var phase: Double? = nil
@Environment(\.accessibilityReduceMotion) private var reduceMotion
@State private var animatedPhase: CGFloat = -1
private var sweep: CGFloat {
if let phase { return -1 + CGFloat(phase) * 2 }
return animatedPhase
}
var body: some View {
RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)
.fill(Color(white: 0.91))
.frame(width: width, height: height)
.overlay {
GeometryReader { geo in
LinearGradient(
colors: [.clear, .white.opacity(0.85), .clear],
startPoint: .leading,
endPoint: .trailing
)
.frame(width: geo.size.width * 0.7)
.offset(x: sweep * geo.size.width * 1.6)
}
.clipShape(RoundedRectangle(cornerRadius: cornerRadius, style: .continuous))
}
.onAppear {
guard !reduceMotion, phase == nil else { return }
withAnimation(.linear(duration: 1.3).repeatForever(autoreverses: false)) {
animatedPhase = 1
}
}
}
}
#Preview {
SkeletonShimmer()
.padding(30)
}
iOS 17 · No dependencies
SwiftUI note. SkeletonBlock is the reusable unit — build your own placeholder rows from it rather than reshaping this one.
Dependencies
- SwiftUI
- No external dependencies
- Flutter
- No external dependencies
Requires iOS 17
Requires Flutter 3.27


