Skeleton
The Skeleton component provides a placeholder loading state, visually indicating where content will appear once data is fetched. It enhances user experience by managing expectations during loading times.
Installation
$ cbui-cli install @crossbuildui/skeleton
cbui-cli
globally and authenticated your account. This will ensure you can access all the necessary features.Import
1import { Skeleton } from '@/crossbuildui/skeleton';
2import type { SkeletonSlotsStyles } from '@/crossbuildui/skeleton'; // Optional: for type safety
expo-linear-gradient
for the shimmer effect. Ensure it's installed in your project:npm install expo-linear-gradient
or yarn add expo-linear-gradient
Skeleton Component
Basic Usage
Control the visibility of the skeleton or actual content using the isLoaded
prop. The style
prop on the Skeleton component defines the shape and size of the placeholder.
1function MyComponent() {
2 const [isLoaded, setIsLoaded] = useState(false);
3
4 useEffect(() => {
5 // Simulate data fetching
6 const timer = setTimeout(() => setIsLoaded(true), 3000);
7 return () => clearTimeout(timer);
8 }, []);
9
10 return (
11 <View>
12 <Skeleton isLoaded={isLoaded} style={{ width: 200, height: 20, borderRadius: 4, marginBottom: 8 }}>
13 <Text style={{ fontSize: 18, fontWeight: 'bold' }}>Content Loaded!</Text>
14 </Skeleton>
15 <Skeleton isLoaded={isLoaded} style={{ width: '80%', height: 16, borderRadius: 4 }}>
16 <Text>This is some paragraph text that appears after loading.</Text>
17 </Skeleton>
18 </View>
19 );
20}
Custom Shapes and Layouts
Apply styles directly via the style
prop to create various shapes (e.g., circles, specific line heights, image placeholders). You can compose multiple Skeletons to mimic complex layouts.
1<View style={{ gap: 16 }}>
2 {/* Circle Skeleton */}
3 <Skeleton isLoaded={false} style={{ width: 50, height: 50, borderRadius: 25 }}>
4 <Image source={{ uri: 'https://via.placeholder.com/50' }} style={{ width: 50, height: 50, borderRadius: 25 }} />
5 </Skeleton>
6
7 {/* Multiple lines for a text block */}
8 <Skeleton isLoaded={false} style={{ width: '90%', height: 18, borderRadius: 4, marginBottom: 6 }}>
9 <Text style={{ fontSize: 16 }}>A line of text.</Text>
10 </Skeleton>
11 <Skeleton isLoaded={false} style={{ width: '70%', height: 18, borderRadius: 4 }}>
12 <Text style={{ fontSize: 16 }}>Another shorter line.</Text>
13 </Skeleton>
14</View>
Disable Animation
If you prefer a static placeholder without the shimmer animation, set disableAnimation
to true.
1<Skeleton
2 isLoaded={false} /* Keep false to show skeleton */
3 disableAnimation
4 style={{ width: 150, height: 20, borderRadius: 4 }}
5>
6 {/* Content won't be shown as isLoaded is false */}
7</Skeleton>
Custom Colors
Override the default theme-based colors for the placeholder background and shimmer effect using placeholderBackgroundColor
and shimmerColor
props.
1<View style={{ gap: 10 }}>
2 <Skeleton
3 isLoaded={false}
4 style={{ width: '100%', height: 24, borderRadius: 4 }}
5 placeholderBackgroundColor="lightblue"
6 shimmerColor="rgba(200, 200, 255, 0.5)"
7 >
8 {/* Content */}
9 </Skeleton>
10 <Skeleton
11 isLoaded={false}
12 style={{ width: '100%', height: 24, borderRadius: 4 }}
13 placeholderBackgroundColor="#555" // Dark placeholder
14 shimmerColor="rgba(150, 150, 150, 0.3)" // Darker shimmer
15 >
16 {/* Content */}
17 </Skeleton>
18</View>
Props Overview
PROP | TYPE | DEFAULT | DESCRIPTION |
---|---|---|---|
children | ReactNode | - | Actual content to display once loading is complete. |
isLoaded | boolean | false | If true, children are rendered; otherwise, skeleton is shown. |
disableAnimation | boolean | false | If true, shimmer animation is disabled. |
style | StyleProp<ViewStyle> | - | Defines the shape/size of the skeleton placeholder. |
styles | SkeletonSlotsStyles | - | Custom styles for internal slots (placeholder, shimmer). |
shimmerColor | string | Theme-based | Color of the shimmer effect (e.g., 'rgba(255,255,255,0.3)'). |
placeholderBackgroundColor | string | Theme-based | Background color of the placeholder. |
Styling
The primary way to define the skeleton's appearance (shape, size) is through the style
prop. This style is applied to the main placeholder view.
For more granular control, the styles
prop accepts an object with keys for internal slots:
placeholder
: Styles applied to the placeholderView
that contains the shimmer. Thestyle
prop is merged with this. Ensureoverflow: 'hidden'
is maintained if overriding.shimmer
: Styles applied to theAnimated.View
containing theLinearGradient
for the shimmer. This view is absolutely positioned to fill the placeholder. The shimmer effect itself comes from the gradient colors, so direct background styling here might obscure it.
style
prop is crucial as it dictates the dimensions and border radius of the skeleton. The placeholder view has overflow: 'hidden'
by default to correctly clip the sweeping shimmer animation.1<Skeleton
2 isLoaded={false}
3 style={{ width: '100%', height: 40, borderWidth: 2, borderColor: 'blue' }} // Base shape
4 styles={{
5 placeholder: { // Styles for the placeholder View
6 opacity: 0.7,
7 },
8 shimmer: { // Styles for the LinearGradient View
9 // Note: Shimmer effect is achieved by LinearGradient colors,
10 // direct backgroundColor here might cover the gradient.
11 // Use for layout or other non-color style adjustments if needed.
12 }
13 }}
14 placeholderBackgroundColor="lightgreen"
15 shimmerColor="rgba(152, 251, 152, 0.6)"
16>
17 {/* Content */}
18</Skeleton>
Theming
The Skeleton component is theme-aware. By default, placeholderBackgroundColor
and shimmerColor
adapt to the current theme (light/dark mode).
- Light Mode Default Placeholder:
themeColors.default['100']
- Dark Mode Default Placeholder:
themeColors.default['200']
- Light Mode Default Shimmer:
'rgba(255, 255, 255, 0.3)'
- Dark Mode Default Shimmer:
'rgba(255, 255, 255, 0.1)'
You can override these defaults by providing explicit values to the placeholderBackgroundColor
and shimmerColor
props.
Accessibility
Skeletons are primarily visual cues for loading states.
- They don't typically have direct interactive roles or explicit accessibility labels themselves, as their purpose is to represent content that is not yet available.
- Ensure that the surrounding context or a screen reader announcement indicates that content is loading. For example, a page title or a live region update could inform users about the loading state.
- Once content is loaded (
isLoaded=
), ensure the actual content (children
) is fully accessible.