Skeleton
A placeholder loading animation to indicate content is being loaded.
The Skeleton component displays a pulsing placeholder while content is loading. It uses React Native's Animated API for a smooth opacity pulse animation and is styled with NativeWind for easy customization.
Preview
Installation
npx novaui-cli add skeletonImport
import { Skeleton } from 'novaui-components'Basic Usage
The Skeleton renders a pulsing rounded-md bg-muted block. Control its shape and size with className:
import { Skeleton } from 'novaui-components'
import { View } from 'react-native'
export function BasicSkeleton() {
return (
<View className="gap-3">
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-4/5" />
<Skeleton className="h-4 w-3/5" />
</View>
)
}Shapes
Rectangle
Default shape for text lines or block content:
<Skeleton className="h-[125px] w-[250px] rounded-md" />Circle
Perfect for avatar placeholders:
<Skeleton className="h-12 w-12 rounded-full" />Rounded
For buttons or badges:
<Skeleton className="h-10 w-24 rounded-md" />Common Patterns
Card Skeleton
A loading placeholder for a card layout:
import { Skeleton } from 'novaui-components'
import { Card, CardHeader, CardContent, CardFooter } from 'novaui-components'
import { View } from 'react-native'
export function CardSkeleton() {
return (
<Card>
<CardHeader>
<Skeleton className="h-6 w-3/4" />
<Skeleton className="h-4 w-1/2" />
</CardHeader>
<CardContent>
<View className="gap-2">
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-4/5" />
</View>
</CardContent>
<CardFooter>
<Skeleton className="h-10 w-24" />
</CardFooter>
</Card>
)
}Profile Skeleton with Avatar
A loading state that transitions into a real Avatar once data is loaded. This shows how Skeleton and Avatar work together:

import { useState, useEffect } from 'react'
import { View, Text } from 'react-native'
import { Skeleton } from 'novaui-components'
import { Avatar, AvatarImage, AvatarFallback } from 'novaui-components'
export function ProfileWithSkeleton() {
const [isLoading, setIsLoading] = useState(true)
useEffect(() => {
// Simulate data fetching
const timer = setTimeout(() => setIsLoading(false), 2000)
return () => clearTimeout(timer)
}, [])
if (isLoading) {
return (
<View className="flex-row items-center gap-4">
<Skeleton className="h-14 w-14 rounded-full" />
<View className="gap-2 flex-1">
<Skeleton className="h-5 w-2/3" />
<Skeleton className="h-4 w-1/3" />
</View>
</View>
)
}
return (
<View className="flex-row items-center gap-4">
<Avatar size="lg">
<AvatarImage src="https://example.com/avatar.jpg" />
<AvatarFallback>JD</AvatarFallback>
</Avatar>
<View>
<Text className="text-base font-medium">John Doe</Text>
<Text className="text-sm text-muted-foreground">@johndoe</Text>
</View>
</View>
)
}List Skeleton
A loading placeholder for a list of items:
import { Skeleton } from 'novaui-components'
import { View } from 'react-native'
export function ListSkeleton() {
return (
<View className="gap-4">
{[1, 2, 3].map((i) => (
<View key={i} className="flex-row items-center gap-3">
<Skeleton className="h-10 w-10 rounded-full" />
<View className="gap-1.5 flex-1">
<Skeleton className="h-4 w-3/4" />
<Skeleton className="h-3 w-1/2" />
</View>
</View>
))}
</View>
)
}Props API
Skeleton Props
Extends React.ComponentPropsWithoutRef<typeof View>:
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | CSS classes to control size, shape, and custom styling |
The base classes applied are rounded-md bg-muted with a pulsing opacity animation.
Animation
The Skeleton uses React Native's built-in Animated API:
- Pulse loop: Opacity animates between
0.5and1.0continuously - Duration: 1000ms per half-cycle (2 seconds full loop)
- Easing:
Easing.out(Easing.ease)for fade-in,Easing.in(Easing.ease)for fade-out - Native driver: Uses
useNativeDriver: truefor optimal performance
Best Practices
-
Match the layout: Shape your skeletons to match the actual content dimensions — this reduces layout shift when content loads
-
Use rounded-full for avatars: Apply
rounded-fullfor circular placeholders -
Vary widths for text: Use different widths (
w-full,w-3/4,w-1/2) across lines for a realistic text appearance -
Group with containers: Wrap skeletons in the same layout components (
Card,View) as the real content -
Don't overuse: Only show skeletons for content that takes noticeable time to load
Related Components
- Use inside
Cardfor card loading states - Combine with
Avatardimensions for profile placeholders - Pair with conditional rendering to swap skeletons for real content