Skeleton
Skeletons are pulsing placeholder blocks that hold the shape of content while it loads. Use one where a spinner cannot — when the placeholder must match the size of the image, avatar, or text line it stands in for.
The block is drawn in surfaceContainerHighest and pulses its opacity on a
repeating timing loop built from theme.motion.durationExtraLong4 and
theme.motion.easingStandard. Under reduced motion the pulse collapses to a
static block. The component is hidden from the accessibility tree — announce
the loading state on the container instead (for example with aria-busy).
Usage
import { Column, Skeleton } from '@rootnative/components'
import { ThemeProvider } from '@rootnative/core'
import { SafeAreaProvider } from 'react-native-safe-area-context'
export default function App() {
return (
<SafeAreaProvider>
<ThemeProvider>
<Column gap="md" style={{ padding: 24 }}>
<Skeleton />
<Skeleton width="80%" />
<Skeleton width="60%" />
</Column>
</ThemeProvider>
</SafeAreaProvider>
)
}
Shapes
shape controls the corner treatment:
| Prop value | Result |
|---|---|
'rounded' (default) | theme.shape.cornerSmall corners |
'circle' | Fully rounded — pass an equal width and height |
'rectangle' | Square corners |
import { Column, Row, Skeleton } from '@rootnative/components'
import { ThemeProvider } from '@rootnative/core'
import { SafeAreaProvider } from 'react-native-safe-area-context'
export default function App() {
return (
<SafeAreaProvider>
<ThemeProvider>
<Row gap="md" align="center" style={{ padding: 24 }}>
<Skeleton shape="circle" width={48} height={48} />
<Column gap="sm" style={{ flex: 1 }}>
<Skeleton height={12} />
<Skeleton width="70%" height={12} />
</Column>
</Row>
</ThemeProvider>
</SafeAreaProvider>
)
}
Card placeholder
Compose skeletons to mirror the layout they replace, then swap in the real content when it arrives:
import { Card, Column, Row, Skeleton } from '@rootnative/components'
import { ThemeProvider } from '@rootnative/core'
import { SafeAreaProvider } from 'react-native-safe-area-context'
export default function App() {
return (
<SafeAreaProvider>
<ThemeProvider>
<Column style={{ padding: 24 }}>
<Card variant="outlined">
<Column p="md" gap="md">
<Row gap="md" align="center">
<Skeleton shape="circle" width={40} height={40} />
<Skeleton width={120} height={20} />
</Row>
<Skeleton height={160} />
<Column gap="sm">
<Skeleton />
<Skeleton width="70%" />
</Column>
</Column>
</Card>
</Column>
</ThemeProvider>
</SafeAreaProvider>
)
}
Static and custom color
Set animated={false} for a block that does not pulse — for example when many
skeletons render at once and the motion becomes noise. containerColor
overrides the block color.
import { Column, Skeleton } from '@rootnative/components'
import { ThemeProvider, useTheme } from '@rootnative/core'
import { SafeAreaProvider } from 'react-native-safe-area-context'
function Demo() {
const theme = useTheme()
return (
<Column gap="md" style={{ padding: 24 }}>
<Skeleton animated={false} />
<Skeleton containerColor={theme.colors.secondaryContainer} />
</Column>
)
}
export default function App() {
return (
<SafeAreaProvider>
<ThemeProvider>
<Demo />
</ThemeProvider>
</SafeAreaProvider>
)
}
Props
| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
width | DimensionValue | 100% | No | Block width, in dp or as a percentage of the parent. |
height | DimensionValue | 16 | No | Block height, in dp or as a percentage of the parent. |
shape | enum | rounded | No | Corner treatment. `'rounded'` uses `theme.shape.cornerSmall`, `'circle'` uses `theme.shape.cornerFull` (pass an equal `width` and `height` for a true circle), `'rectangle'` uses square corners. |
containerColor | string | theme.colors.surfaceContainerHighest | No | Override the block color. |
animated | boolean | | No | Run the opacity pulse. Set `false` for a static block — for example when many skeletons render at once and one pulse is enough. |
onKeyDown | (event: { nativeEvent: { key?: string; }; }) => void | - | No | - |