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

Import

MyComponent.tsx
1import { Skeleton } from '@/crossbuildui/skeleton'; 2import type { SkeletonSlotsStyles } from '@/crossbuildui/skeleton'; // Optional: for type safety

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.

MyComponent.tsx
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.

MyComponent.tsx
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.

MyComponent.tsx
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.

MyComponent.tsx
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

PROPTYPEDEFAULTDESCRIPTION
childrenReactNode-Actual content to display once loading is complete.
isLoadedbooleanfalseIf true, children are rendered; otherwise, skeleton is shown.
disableAnimationbooleanfalseIf true, shimmer animation is disabled.
styleStyleProp<ViewStyle>-Defines the shape/size of the skeleton placeholder.
stylesSkeletonSlotsStyles-Custom styles for internal slots (placeholder, shimmer).
shimmerColorstringTheme-basedColor of the shimmer effect (e.g., 'rgba(255,255,255,0.3)').
placeholderBackgroundColorstringTheme-basedBackground 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 placeholder View that contains the shimmer. The style prop is merged with this. Ensure overflow: 'hidden' is maintained if overriding.
  • shimmer: Styles applied to the Animated.View containing the LinearGradient 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.
MyComponent.tsx
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.