---
description: This rule provides comprehensive guidelines for generating skeleton UI components using react-native-fast-shimmer, ensuring pixel-perfect matching with target components' layouts, dimensions, and element positioning.
globs: *.js,*.jsx,*.ts,*.tsx
---
This rule ensures that when generating skeleton UI components, they perfectly match the target component's layout, dimensions, and styling while using the custom Shimmer component with semantic subcomponents (Shimmer.Text, Shimmer.Image, Shimmer).
Before creating any skeleton component, you MUST:
-
Read and fully understand the target component by examining:
- Complete component structure and JSX hierarchy
- All styling properties (dimensions, margins, padding, positioning)
- Layout patterns (flexbox, grid, absolute positioning)
- Element spacing and alignment
- Conditional rendering logic that affects layout
- Typography styles (fontSize, lineHeight, fontWeight)
-
Identify all visual elements that need skeleton representation:
- Text blocks (titles, paragraphs, labels)
- Images and icons
- Buttons and interactive elements
- Cards and containers
- Lists and repeated elements
- Spacing between elements
Create a new file named {TargetComponent}Skeleton.tsx
where {TargetComponent}
matches the exact name of the target component.
Example:
- Target:
HabitListHeader.tsx
→ Skeleton:HabitListHeaderSkeleton.tsx
- Target:
UserProfile.tsx
→ Skeleton:UserProfileSkeleton.tsx
- Target:
ChatMessage.tsx
→ Skeleton:ChatMessageSkeleton.tsx
Choose the appropriate components based on content and layout:
Layout Components:
- Box: For containers, wrappers, and complex layout structures
- VStack: For vertical stacking with consistent spacing
- HStack: For horizontal arrangement with consistent spacing
Shimmer Components:
- Shimmer.Text: For text content (titles, paragraphs, labels, buttons with text)
- Shimmer.Image: For visual content (images, avatars, icons, graphics)
- Shimmer: For generic containers, dividers, or complex shapes
Always include these imports in skeleton components:
import React from 'react';
import { Shimmer } from '~/components/atoms/Shimmer';
import { Box } from '~/components/layouts/Box';
import { HStack } from '~/components/layouts/HStack';
import { VStack } from '~/components/layouts/VStack';
Use the appropriate Shimmer subcomponents with proper props:
// For text elements
<Shimmer.Text width={120} height={16} />
<Shimmer.Text width="80%" height={14} />
// For image elements
<Shimmer.Image width={100} height={100} borderRadius={8} />
<Shimmer.Image width="100%" height={200} borderRadius={12} />
// For button elements
<Shimmer width={120} height={40} borderRadius={20} />
// For generic containers
<Shimmer width="100%" height={60} borderRadius={8} />
- Copy ALL dimension-related styles from the target component
- Maintain identical:
width
,height
,minWidth
,maxWidth
,minHeight
,maxHeight
- Preserve aspect ratios for images and media elements
- Use layout component spacing: Prefer
spacing
prop onHStack
/VStack
over manual margins - Replicate container spacing: Copy
padding
values for Box containers - Match exact spacing: Use consistent spacing values that match the target component
- Avoid manual margins: Use layout component spacing instead of
marginTop
,marginBottom
, etc.
- Maintain identical positioning:
position
,top
,left
,right
,bottom
- Copy z-index values:
zIndex
- Preserve alignment properties:
alignItems
,justifyContent
,alignSelf
- Use semantic layout components: Replace
flexDirection: 'row'
withHStack
,flexDirection: 'column'
withVStack
- Leverage spacing props: Use
spacing
on layout components instead of gap or margins - Preserve alignment: Copy
alignItems
,justifyContent
when needed, but prefer layout component defaults - Container styling: Use
Box
for complex containers with custom styling
// For single-line text (titles, labels)
<Shimmer.Text width={120} height={20} />
// For paragraph text
<Shimmer.Text width="100%" height={16} />
// For multi-line text (replicate each line with proper spacing)
<VStack spacing={4}>
<Shimmer.Text width="100%" height={16} />
<Shimmer.Text width="85%" height={16} />
<Shimmer.Text width="60%" height={16} />
</VStack>
// For smaller labels or captions
<Shimmer.Text width={80} height={12} />
// For large images or banners
<Shimmer.Image width="100%" height={200} borderRadius={12} />
// For square images or thumbnails
<Shimmer.Image width={100} height={100} borderRadius={8} />
// For profile avatars (circular)
<Shimmer.Image width={60} height={60} borderRadius={30} />
// For small icons
<Shimmer.Image width={24} height={24} borderRadius={4} />
// For primary buttons
<Shimmer width={120} height={44} borderRadius={22} />
// For secondary buttons
<Shimmer width={100} height={36} borderRadius={18} />
// For icon buttons
<Shimmer width={44} height={44} borderRadius={22} />
// For tab buttons
<Shimmer width={80} height={32} borderRadius={16} />
// For cards or containers
<Shimmer width="100%" height={120} borderRadius={12} />
// For dividers
<Shimmer width="100%" height={1} borderRadius={0} />
// For small containers
<Shimmer width={60} height={20} borderRadius={10} />
Maintain the exact container hierarchy using layout components and proper Shimmer props:
export const TargetComponentSkeleton = () => {
return (
<Box style={{ padding: 16 }}>
{/* Header section */}
<VStack spacing={8}>
<Shimmer.Text width={200} height={24} />
<Shimmer.Text width={150} height={16} />
</VStack>
{/* Content section */}
<HStack spacing={12} style={{ marginTop: 16 }}>
<Shimmer.Image width={80} height={80} borderRadius={8} />
<VStack spacing={4} style={{ flex: 1 }}>
<Shimmer.Text width="100%" height={16} />
<Shimmer.Text width="70%" height={16} />
</VStack>
</HStack>
</Box>
);
};
The Shimmer component handles styling internally, focus on layout and dimensions:
export const TargetComponentSkeleton = () => {
return (
<Box style={{
flex: 1,
padding: 16,
backgroundColor: 'transparent'
}}>
{/* Use specific width/height/borderRadius props instead of styles */}
<Shimmer.Text width={200} height={24} />
<Shimmer.Image width={100} height={100} borderRadius={8} />
<Shimmer width={120} height={40} borderRadius={20} />
</Box>
);
};
// No separate StyleSheet needed for shimmer elements
// Only use styles for layout containers when necessary
- Width: Use exact pixel values for fixed elements, percentage for flexible elements
- Height: Always use pixel values for consistent visual weight
- BorderRadius: Match target component's border radius exactly
// ✅ Good: Specific dimensions
<Shimmer.Text width={150} height={20} />
// ✅ Good: Flexible width with fixed height
<Shimmer.Text width="100%" height={16} />
// ✅ Good: Circular elements
<Shimmer.Image width={60} height={60} borderRadius={30} />
// ❌ Avoid: No height specified (uses default)
<Shimmer.Text width={150} />
// ❌ Avoid: String heights
<Shimmer.Text width={150} height="20px" />
Before completing skeleton generation, verify:
- Dimensional Accuracy: All skeleton elements match target dimensions exactly
- Layout Structure: Container hierarchy and nesting match the target
- Spacing Consistency: All margins, padding, and gaps are identical
- Element Count: Number of skeleton elements matches visible elements in target
- Positioning: All elements are positioned identically to the target
- Typography Simulation: Text skeleton heights match target line heights
- Interactive Elements: Buttons and clickable areas are properly represented
- Responsive Behavior: Skeleton maintains target's responsive characteristics
- Animation Smoothness: Shimmer effect operates smoothly across all elements
Integrate skeleton into the target component using this pattern:
// In the target component
import { TargetComponentSkeleton } from './TargetComponentSkeleton';
const TargetComponent = () => {
const { data, isLoading } = useQuery(...);
if (isLoading) {
return <TargetComponentSkeleton />;
}
return (
// Actual component content
);
};
If the target component accepts styling props, ensure skeleton supports them:
interface SkeletonProps {
style?: ViewStyle | ViewStyle[];
// Include any layout-affecting props from target component
}
export const TargetComponentSkeleton = ({ style, ...props }: SkeletonProps) => {
return (
<View style={[styles.container, style]}>
{/* Skeleton content */}
</View>
);
};
- Use
StyleSheet.create
for all skeleton styles - Minimize the number of nested
Shimmer
components when possible - Reuse common skeleton styles across similar elements
- Consider memoization for complex skeleton layouts
- Avoid creating unnecessary animated values
- Use consistent shimmer configurations across related components
- Clean up any custom animations properly
For components that render in lists:
export const ListItemSkeleton = () => (
<HStack spacing={12} style={{ padding: 16 }}>
<Shimmer.Image width={50} height={50} borderRadius={25} />
<VStack spacing={4} style={{ flex: 1 }}>
<Shimmer.Text width="80%" height={18} />
<Shimmer.Text width="60%" height={14} />
</VStack>
<Shimmer width={24} height={24} borderRadius={4} />
</HStack>
);
For card-based layouts:
export const CardSkeleton = () => (
<Box style={{ borderRadius: 12, overflow: 'hidden' }}>
<Shimmer.Image width="100%" height={180} borderRadius={0} />
<VStack spacing={8} style={{ padding: 16 }}>
<Shimmer.Text width="90%" height={20} />
<Shimmer.Text width="100%" height={16} />
<Shimmer.Text width="70%" height={16} />
<HStack spacing={8} style={{ marginTop: 8 }}>
<Shimmer width={100} height={36} borderRadius={18} />
<Shimmer width={80} height={36} borderRadius={18} />
</HStack>
</VStack>
</Box>
);
For user profile layouts:
export const ProfileSkeleton = () => (
<VStack spacing={16} style={{ padding: 20, alignItems: 'center' }}>
<Shimmer.Image width={100} height={100} borderRadius={50} />
<VStack spacing={8} style={{ alignItems: 'center' }}>
<Shimmer.Text width={150} height={22} />
<Shimmer.Text width={200} height={16} />
<HStack spacing={20} style={{ marginTop: 12 }}>
<VStack spacing={4} style={{ alignItems: 'center' }}>
<Shimmer.Text width={40} height={20} />
<Shimmer.Text width={60} height={14} />
</VStack>
<VStack spacing={4} style={{ alignItems: 'center' }}>
<Shimmer.Text width={40} height={20} />
<Shimmer.Text width={60} height={14} />
</VStack>
<VStack spacing={4} style={{ alignItems: 'center' }}>
<Shimmer.Text width={40} height={20} />
<Shimmer.Text width={60} height={14} />
</VStack>
</HStack>
</VStack>
</VStack>
);
- Approximate Sizing: Never estimate dimensions - always measure exactly
- Missing Spacing: Don't forget to include margins and padding
- Wrong Hierarchy: Maintain exact component structure and nesting
- Inconsistent Border Radius: Match border radius values precisely
- Typography Mismatch: Ensure text skeleton heights match actual text heights
- Container Overflow: Respect container boundaries and overflow settings
- Visual Comparison: Compare skeleton side-by-side with target component
- Layout Measurement: Verify dimensions using development tools
- Spacing Verification: Check all margin and padding values
- Animation Testing: Ensure shimmer effect works smoothly
- Edge Case Testing: Test with different screen sizes and orientations
When creating skeleton components, include:
/**
* Skeleton component for {TargetComponent}
*
* Provides loading state representation with identical layout and dimensions
* to the target component. Uses custom Shimmer component with subcomponents
* (Shimmer.Text, Shimmer.Image, Shimmer) for semantic skeleton generation.
*
* @param style - Optional styling overrides
* @returns JSX element matching target component layout
*/
This rule ensures that every skeleton component generated will be a pixel-perfect representation of its target, providing seamless loading experiences for users while maintaining design consistency across the application. The semantic use of Shimmer subcomponents (Text, Image, generic) ensures proper skeleton representation that matches the content type of each element.