💀 Compose Skeleton Pack
A feature-rich, Kotlin Multiplatform Skeleton & Shimmer library for Android, Desktop & iOS with advanced animation controls.

✨ Features
📸 Screenshots
| Feed Screen | Profile Screen | Dashboard Screen |
|---|
 |  |  |

🚀 Installation

Add the dependency to your module's build.gradle.kts:
dependencies {
implementation("io.github.ebinjoy999:compose-skeleton-shimmer:2.0.1")
}
Or using version catalog (libs.versions.toml):
[versions]
skeleton = "2.0.1"
[libraries]
skeleton-core = { group = "io.github.ebinjoy999", name = "compose-skeleton-shimmer", version.ref = "skeleton" }
📖 Quick Start
Basic Skeleton Components
SkeletonBox(
modifier = Modifier
.fillMaxWidth()
.height(100.dp),
shape = RoundedCornerShape(12.dp)
)
SkeletonCircle(size = 64.dp)
SkeletonLine(
modifier = Modifier.fillMaxWidth(0.7f),
height = 16.dp
)
Pre-built Components
SkeletonCard(
imageHeight = 180.dp,
titleLines = 1,
showDescription = true,
descriptionLines = 2
)
SkeletonListItem(
leadingSize = 48.dp,
isLeadingCircle = true,
showSubtitle = true
)
SkeletonProfile(
avatarSize = dp,
showBio = ,
actionButtonCount =
)
SkeletonTile(
modifier = Modifier.aspectRatio(),
showLabel =
)
Skeleton Visibility Controller
Skeleton(
isLoading = viewModel.isLoading,
skeleton = { SkeletonCard() }
) {
ActualCard(data = viewModel.data)
}
Skeleton(
isLoading = isLoading,
transition = SkeletonTransition.Crossfade,
transitionDurationMs = 400,
skeleton = { SkeletonProfile() }
) {
UserProfile(user = user)
}
Nullable Data Helper
SkeletonIfNull(
data = user,
skeleton = { SkeletonProfile() }
) { user ->
UserProfile(user = user)
}
List Skeleton Support
LazyColumn {
skeletonItems(
isLoading = isLoading,
count = 5
) {
SkeletonListItem()
}
items(actualItems) { item ->
ListItem(item)
}
}
Grid Skeleton Support
LazyVerticalGrid(columns = GridCells.Fixed(2)) {
skeletonGridItems(
isLoading = isLoading,
count = 6
) {
SkeletonTile(
modifier = Modifier.aspectRatio(1f)
)
}
}
🎨 Shimmer Customization
Basic Shimmer Modifier
Box(
modifier = Modifier
.size(100.dp)
.background(Color.LightGray)
.shimmer()
)
Custom Shimmer State
val shimmerState = rememberShimmerState(
durationMillis = 1200,
direction = ShimmerDirection.LeftToRight
)
SkeletonCard(shimmerState = shimmerState)
SkeletonListItem(shimmerState = shimmerState)
Shimmer Directions
rememberShimmerState(direction = ShimmerDirection.LeftToRight)
rememberShimmerState(direction = ShimmerDirection.RightToLeft)
rememberShimmerState(direction = ShimmerDirection.TopToBottom)
rememberShimmerState(direction = ShimmerDirection.BottomToTop)
🎭 Advanced Shimmer Effects
Shimmer Types
Choose from four distinct shimmer animation styles:
val linearConfig = ShimmerConfig(shimmerType = ShimmerType.Linear)
val radialConfig = ShimmerConfig(shimmerType = ShimmerType.Radial)
val pulseConfig = ShimmerConfig(shimmerType = ShimmerType.Pulse)
val waveConfig = ShimmerConfig(shimmerType = ShimmerType.Wave, waveCount = 3)
Pre-built Config Presets
ShimmerConfig.Default
ShimmerConfig.Subtle
ShimmerConfig.Prominent
ShimmerConfig.Pulse
ShimmerConfig.Spotlight
ShimmerConfig.MultiWave
ShimmerConfig.Accessible
Custom Shimmer Configuration
val customConfig = ShimmerConfig(
shimmerType = ShimmerType.Linear,
direction = ShimmerDirection.LeftToRight,
durationMillis = 1200,
angle = 20f,
shimmerWidth = 200f,
intensity = 0.7f,
dropOff = ShimmerDropOff.Soft,
easing = ShimmerEasing.EaseInOut,
repeatMode = ShimmerRepeatMode.Restart,
staggerDelayMillis = ,
respectReducedMotion =
)
shimmerState = rememberShimmerState(config = customConfig)
SkeletonCard(shimmerState = shimmerState)
Shimmer Angle & Tilt
val angledConfig = ShimmerConfig(
angle = 30f
)
Drop-off Styles
Control how the shimmer gradient fades at edges:
ShimmerDropOff.Linear
ShimmerDropOff.Soft
ShimmerDropOff.Sharp
Easing Functions
ShimmerEasing.Linear
ShimmerEasing.EaseIn
ShimmerEasing.EaseOut
ShimmerEasing.EaseInOut
ShimmerEasing.Spring
⏸️ Shimmer Control
Global Pause/Resume
Pause all shimmer animations for battery optimization:
val controller = rememberShimmerController()
controller.pause()
controller.resume()
val shimmerState = rememberShimmerState(controller = controller)
Lifecycle-Aware Shimmer
Automatically pause when app goes to background:
val shimmerState = rememberLifecycleAwareShimmerState(
config = ShimmerConfig.Default
)
Limited Iterations
Stop shimmer after N iterations:
val shimmerState = rememberLimitedShimmerState(
maxIterations = 5,
onComplete = { }
)
Animation Callbacks
val shimmerState = rememberShimmerStateWithCallbacks(
onAnimationStart = { },
onAnimationIteration = { iteration -> },
onAnimationEnd = { }
)
🔄 Staggered Animations
Create cascading shimmer effects for list items:
val shimmerState1 = rememberShimmerState(
config = ShimmerConfig(staggerDelayMillis = 0)
)
val shimmerState2 = rememberShimmerState(
config = ShimmerConfig(staggerDelayMillis = 100)
)
val shimmerState3 = rememberShimmerState(
config = ShimmerConfig(staggerDelayMillis = 200)
)
val states = rememberStaggeredShimmerStates(
count = 5,
staggerDelayMillis = 100,
config = ShimmerConfig.Default
)
states.forEachIndexed { index, state ->
SkeletonListItem(shimmerState = state)
}
Custom Colors
val shimmerState = rememberShimmerState(
baseColor = Color(0xFFE0E0E0),
highlightColor = Color(0xFFF5F5F5)
)
Custom Shimmer Brush
Box(
modifier = Modifier
.size(100.dp)
.shimmerWithBrush { progress, size ->
Brush.linearGradient(
colors = listOf(Color.Red, Color.Blue, Color.Red),
start = Offset(-size.width + size.width * 2 * progress, 0f),
end = Offset(size.width * 2 * progress, size.height)
)
}
)
🎨 Theming
Default Theme Colors
The library automatically adapts to light/dark mode:
baseColor = Color(0xFFE0E0E0)
highlightColor = Color(0xFFF5F5F5)
baseColor = Color(0xFF3A3A3A)
highlightColor = Color(0xFF4A4A4A)
Material 3 Integration
val colors = materialSkeletonColors()
SkeletonTheme(colors = colors) {
SkeletonCard()
}
Custom Theme
val customColors = customSkeletonColors(
baseColor = MaterialTheme.colorScheme.surfaceVariant,
highlightColor = MaterialTheme.colorScheme.surface
)
SkeletonTheme(colors = customColors) {
SkeletonCard()
SkeletonListItem()
}
♿ Accessibility
Reduced Motion Support
Automatically respect system accessibility settings:
val config = ShimmerConfig(respectReducedMotion = true)
val accessibleState = rememberShimmerStateWithPreset(ShimmerConfig.Accessible)
val reduceMotion = rememberReduceMotionEnabled()
if (reduceMotion) {
}
Semantic Descriptions
Add screen reader support:
SkeletonBox(
modifier = Modifier
.skeletonSemantics(
type = SkeletonSemanticType.Image,
description = "Loading profile picture"
)
)
Pre-built Accessibility Helpers
val accessibleConfig = rememberAccessibleShimmerConfig()
📁 Project Structure
⚡ Performance Notes
Best Practices
What We Avoid
- ❌ No bitmap usage
- ❌ No reflection
- ❌ No unnecessary allocations during animation
- ❌ No blocking operations
🤔 Why Skeleton Loading?
Skeleton screens (also known as "content placeholders") improve perceived performance and user experience:
Research shows that skeleton screens can reduce perceived wait time by up to 30% compared to traditional spinners.
📋 API Reference
Shimmer Configuration
Skeleton Components
Controllers
State Functions
Modifiers
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature)
- Commit your changes (
git commit -m 'Add amazing feature')
- Push to the branch (
git push origin feature/amazing-feature)
- Open a Pull Request
📄 License
Copyright 2026 Ebin
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http:
Unless applicable law agreed to writing, software
distributed under the License distributed an BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express implied.
See the License the specific language governing permissions
limitations under the License.
🙏 Acknowledgments
- Jetpack Compose - Modern Android UI toolkit
- Material 3 - Design system
- Facebook Shimmer - Inspiration for shimmer effect