Skeleton loading is a popular pattern for improving perceived performance by showing placeholders that mimic content layout during loading. While SwiftUI doesn’t support this out of the box, this SDK makes it easy to add skeleton views to any SwiftUI interface.



With minimal setup, you can display animated or static placeholders that blend seamlessly into your design—improving user experience during data fetches or slow content loads.
-
ShimmerView: A ready-to-use view that displays animated shimmering placeholders, perfect for representing loading content with minimal setup.
-
.shimmer: A flexible modifier that applies the shimmer effect to any SwiftUI view, allowing full customization of layout, shape, and animation.
Platform | Minimum Version |
---|---|
iOS | 15.0 |
macOS | 14.0 |
tvOS | 17.0 |
watchOS | 10.0 |
visionOS | 1.0 |
- Open your project in Xcode.
- Navigate to
File
>Swift Packages
>Add Package Dependency
. - Paste the repository URL:
https://github.com/Enryun/JTSkeleton
- Follow the prompts to add the package to your project.
For more details on using Swift Package Manager, visit Apple's Swift Package Manager documentation.
A view displaying a shimmering loading placeholder.
ShimmerView()
This view simulates a 'shimmer' effect commonly used as a placeholder during content loading. It consists of multiple shimmering elements: a pair of small circular views at the top and bottom, and larger rectangular views in between, all showcasing the shimmer effect.
iOS
:
CommonSwiftUI_ShimmerView.mp4
MacOS
:
Screen.Recording.2025-04-25.at.19.07.07.mov
No additional configuration is needed. The shimmer effect starts automatically, simulating content loading in your UI.
Visibility:
- Manage the visibility using
.opacity()
modifier orif-else
conditions based on your application's state. This helps integrate the indicator seamlessly into your UI or hide it when not needed.
For more customization, look at the view modifier to apply a shimmer effect to any SwiftUI view.
Applies a shimmer effect to any SwiftUI view, enhancing the UI with a dynamic loading indicator.
Text("Loading...")
.shimmer(isActive: $isShimmer, tint: .gray.opacity(0.3), highlight: .white, blur: 5, redacted: true)
Text("Loading...")
.shimmer(tint: .gray.opacity(0.3), highlight: .white, blur: 5, redacted: false)
Parameters:
isActive
: ABinding<Bool>
that controls whether the shimmer effect is active.tint
: The background color of the shimmer.highlight
: The color of the shimmering highlight.blur
: The amount of blur applied to the shimmer effect. Default is 0.highlightOpacity
: The opacity of the shimmer highlight. Default is 1.speed
: The speed of the shimmer effect. Default is case .medium for 2 second.redacted
: A Boolean value that indicates whether the view's content should be redacted during the shimmer effect. Default is false.
This function overlays a shimmer animation over the view it modifies, typically used as a placeholder during content loading. The effect can be extensively customized to match your app's style.
Example loading redacted and non-redacted:
VStack {
HStack {
Circle()
.frame(width: 55, height: 55)
VStack(alignment: .leading, spacing: 6) {
RoundedRectangle(cornerRadius: 4)
.frame(height: 10)
RoundedRectangle(cornerRadius: 4)
.frame(height: 10)
.padding(.trailing, 50)
RoundedRectangle(cornerRadius: 4)
.frame(height: 10)
.padding(.trailing, 100)
}
}
.padding(15)
.padding(.horizontal, 30)
.shimmer(tint: .gray.opacity(0.3), highlight: .white, blur: 5, redacted: true)
Text("Loading...")
.shimmer(tint: .gray.opacity(0.3), highlight: .white, blur: 5, redacted: false)
}
Simulator.Screen.Recording.-.iPhone.15.Pro.-.2024-09-16.at.16.37.33.mp4
Example loading with Binding
isActive to control loading:
@State private var isShimmer = true
var body: some View {
VStack {
HStack {
Circle()
.frame(width: 55, height: 55)
VStack(alignment: .leading, spacing: 6) {
RoundedRectangle(cornerRadius: 4)
.frame(height: 10)
RoundedRectangle(cornerRadius: 4)
.frame(height: 10)
.padding(.trailing, 50)
RoundedRectangle(cornerRadius: 4)
.frame(height: 10)
.padding(.trailing, 100)
}
}
.padding(15)
.padding(.horizontal, 30)
.shimmer(isActive: $isShimmer, tint: .gray.opacity(0.3), highlight: .white, blur: 5, redacted: true)
Text("Loading...")
.fontWeight(.semibold)
.shimmer(isActive: $isShimmer, tint: .gray.opacity(0.3), highlight: .white, blur: 5, redacted: false)
}
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
isShimmer = false
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
isShimmer = true
}
}
}
CommonSwiftUI_ShimerModifierActive.mp4
Customize the parameters to fit the style of your app's loading indicators.
James Thang, find me on LinkedIn
Learn more about SwiftUI, check out my book 📚 on Amazon