react-native-shimmer-craft
v1.0.0
Published
Lightweight, zero-dependency shimmer and auto-inference skeleton UI engine for React Native & Expo
Maintainers
Readme
react-native-shimmer-craft
react-native-shimmer-craft is a lightweight, zero-runtime-dependency shimmer and auto-inference skeleton UI engine for React Native, Expo, and React Native Web.
It automatically inspects your component tree to generate matching skeleton loading placeholders with near-zero layout shift, while also providing composable primitives for building custom skeleton layouts.
✨ Features
- 🪄 Auto-Inference Mode: Automatically converts real React Native JSX trees into visually matched skeleton layouts.
- 🧱 Explicit Primitives: Compose bespoke skeleton placeholders with
ShimmerCraft.Text,ShimmerCraft.Circle, andShimmerCraft.Rect. - ⚡ Zero Runtime Dependencies: No
react-native-reanimated, no native gradient modules, and zero native linking. Works out of the box in Expo Go and bare React Native. - 🚀 60fps Native Driver Animation: High-performance animated sweep running completely outside of React's state render loop.
- 🔄 Synchronized Shimmer Clock: A single root animation coordinator powers 100+ skeleton items in a tree with minimal CPU & memory overhead.
- 🧭 Multi-Directional Sweep: Supports
left-to-right,right-to-left,top-to-bottom, anddiagonal. - 🌗 Dark Mode & Palette Overrides: Automatically detects system appearance or allows manual overrides and custom color themes.
- 🎯 Per-Element Overrides: Customize specific child nodes with
shimmerType,shimmerWidth, andshimmerHeight. - ♿ Accessible & Reduced Motion: Automatically hides placeholder nodes from screen readers and respects system reduced motion settings.
- 📱 100% Cross-Platform: iOS, Android, React Native Web, Expo, and React Native CLI.
📦 Installation
npm install react-native-shimmer-craftor using yarn:
yarn add react-native-shimmer-craftPeer Dependencies
Ensure you have react (>=17.0.0) and react-native (>=0.65.0) installed in your project.
Note: No native linking or Podfile changes are required!
🚀 Quick Start
Mode A: Auto-Inference Mode
Wrap any component tree with <ShimmerCraft isLoading={isLoading}>. When loading is active, the engine inspects the layout styles and element hierarchy to construct an aligned skeleton UI:
import React, { useState } from 'react';
import { View, Text, Image, StyleSheet } from 'react-native';
import ShimmerCraft from 'react-native-shimmer-craft';
export default function UserProfileCard() {
const [isLoading, setIsLoading] = useState(true);
return (
<ShimmerCraft isLoading={isLoading} duration={1200}>
<View style={styles.card}>
<Image
source={{ uri: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb' }}
style={styles.avatar}
/>
<View style={styles.content}>
<Text style={styles.name}>Sophia Chen</Text>
<Text style={styles.role}>Mobile Architect</Text>
</View>
</View>
</ShimmerCraft>
);
}
const styles = StyleSheet.create({
card: { flexDirection: 'row', padding: 16, alignItems: 'center' },
avatar: { width: 56, height: 56, borderRadius: 28 },
content: { marginLeft: 12, flex: 1 },
name: { fontSize: 16, fontWeight: '700' },
role: { fontSize: 13, color: '#666', marginTop: 4 },
});When isLoading={false}, your original component tree is rendered untouched.
Mode B: Explicit Primitives
Construct fine-tuned loading placeholders manually using compound primitives:
import React from 'react';
import { View } from 'react-native';
import ShimmerCraft from 'react-native-shimmer-craft';
export function CustomSkeleton() {
return (
<ShimmerCraft isLoading={true} direction="diagonal">
<View style={{ padding: 16 }}>
{/* Avatar */}
<ShimmerCraft.Circle size={64} />
{/* Text lines */}
<View style={{ marginTop: 16 }}>
<ShimmerCraft.Text width="75%" height={16} />
<View style={{ height: 8 }} />
<ShimmerCraft.Text lines={3} height={12} gap={6} lastLineWidth="50%" />
</View>
{/* Card or Banner */}
<View style={{ marginTop: 16 }}>
<ShimmerCraft.Rect width="100%" height={140} borderRadius={12} />
</View>
</View>
</ShimmerCraft>
);
}You can also import primitives as named exports:
import { ShimmerText, ShimmerCircle, ShimmerRect } from 'react-native-shimmer-craft';🛠️ Child Overrides in Auto-Inference
In Auto-Inference mode, you can override how specific elements are translated into skeletons:
<ShimmerCraft isLoading={isLoading}>
<View style={styles.container}>
{/* Force element to render as a circle skeleton */}
<View shimmerType="circle" shimmerWidth={48} shimmerHeight={48} />
{/* Force element to render as a text bar with specific width */}
<View shimmerType="text" shimmerWidth="60%" />
{/* Force element to render as a rectangle */}
<View shimmerType="rect" shimmerHeight={120} />
{/* Reserve layout dimensions without applying any shimmer animation */}
<View shimmerType="none" style={{ width: 100, height: 40 }} />
</View>
</ShimmerCraft>Override Priority Order
- Explicit child shimmer props (
shimmerType,shimmerWidth,shimmerHeight) - Explicit style dimensions (
width,height,borderRadius) - Automatic element inference (
<Text>,<Image>,<View>) - Safe fallback defaults
🧭 Shimmer Directions
Support for 4 sweep directions:
<ShimmerCraft direction="left-to-right" /> {/* Default */}
<ShimmerCraft direction="right-to-left" />
<ShimmerCraft direction="top-to-bottom" />
<ShimmerCraft direction="diagonal" />🌗 Dark Mode & Themes
By default, react-native-shimmer-craft detects the operating system color scheme using useColorScheme().
You can explicitly control the theme and configure custom palettes:
<ShimmerCraft
isLoading={true}
useDarkMode={true}
shimmerColors={['#E5E7EB', '#F9FAFB', '#E5E7EB']}
darkShimmerColors={['#1E293B', '#334155', '#1E293B']}
>
<UserProfile />
</ShimmerCraft>📖 API Reference
<ShimmerCraft /> Props
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| isLoading | boolean | true | When true, displays skeleton UI. When false, displays original children. |
| children | ReactNode | — | Component tree to render or infer skeletons from. |
| fallback | ReactNode | undefined | Custom skeleton component to render when isLoading=true (bypasses auto-inference). |
| direction | 'left-to-right' \| 'right-to-left' \| 'top-to-bottom' \| 'diagonal' | 'left-to-right' | Direction of the shimmer animation sweep. |
| duration | number | 1200 | Duration in milliseconds of one animation loop cycle. |
| useDarkMode | boolean | undefined | Manual dark mode toggle. If omitted, uses system appearance. |
| shimmerColors | string[] | ['#E0E0E0', '#F5F5F5', '#E0E0E0'] | Light mode color palette [base, highlight, base]. |
| darkShimmerColors | string[] | ['#2A2A2A', '#3F3F3F', '#2A2A2A'] | Dark mode color palette [base, highlight, base]. |
| autoBorderRadius | number | 6 | Default corner radius for auto-inferred skeleton shapes. |
| style | StyleProp<ViewStyle> | undefined | Container style. |
| testID | string | undefined | Test identifier. |
<ShimmerCraft.Text /> Props
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| width | number \| string | '100%' | Width of the text bar(s) (e.g. 200 or '75%'). |
| height | number | 14 | Height of each text line. |
| borderRadius| number | 4 | Corner radius of the text bar. |
| lines | number | 1 | Number of lines to generate for multi-line text skeletons. |
| gap | number | 8 | Spacing between lines when lines > 1. |
| lastLineWidth | number \| string | '60%' | Width of the final line when lines > 1. |
| style | StyleProp<ViewStyle> | undefined | Custom container style. |
<ShimmerCraft.Circle /> Props
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| size | number | 48 | Diameter of the circle (sets width, height, and borderRadius = size/2). |
| width | number \| string | 48 | Explicit width override. |
| height | number \| string | 48 | Explicit height override. |
| style | StyleProp<ViewStyle> | undefined | Custom container style. |
<ShimmerCraft.Rect /> Props
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| width | number \| string | '100%' | Width of the rectangle. |
| height | number \| string | 80 | Height of the rectangle. |
| borderRadius| number | 6 | Corner radius. |
| style | StyleProp<ViewStyle> | undefined | Custom container style. |
⚡ Performance & Architecture
- Single Animation Clock: Rather than instantiating an independent
Animated.Valueand interval per node,<ShimmerCraft>shares an animation clock across all descendant nodes via React Context. - Zero Frame State Updates: The shimmer sweep relies entirely on
Animated.timingwithuseNativeDriver: true(or standard web CSS translations on React Native Web). React renders zero times during the animation loop. - Memoized Tree Inspection: Component parsing is memoized during render cycles, ensuring fast 60fps performance even for large screens with 100+ skeleton items.
♿ Accessibility
- All skeleton placeholders are automatically annotated with
accessible={false},accessibilityElementsHidden={true}, andimportantForAccessibility="no-hide-descendants"to ensure screen readers do not read placeholder elements. - When
isLoading={false}, your real content retains full accessibility. - Reduced Motion: Automatically integrates with React Native's
AccessibilityInfo.isReduceMotionEnabled()to disable looping highlight translations when the user prefers reduced motion.
⚠️ Auto-Inference Limitations & Best Practices
React Native does not expose the rendered pixel layout dimensions of arbitrary custom components prior to measurement on screen.
How Auto-Inference Works
- The parser inspects the JSX element tree, styles (
StyleSheet.flatten), and explicit dimensions. - It preserves layout containers (
flex,flexDirection,margin,padding,gap,borderRadius, etc.) while replacing content nodes (<Text>,<Image>, leaf<View>) with shimmer placeholders.
When to Use Child Overrides or Primitives
- If you have an opaque custom component that doesn't expose inline style dimensions (e.g.
<CustomIcon />), provide explicit dimensions or overrides:<CustomAvatar shimmerType="circle" shimmerWidth={56} shimmerHeight={56} /> - For highly custom or irregular loading screens, use explicit primitives (
ShimmerCraft.Text,Circle,Rect) or thefallback={<CustomSkeleton />}prop.
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request or open an Issue.
- Fork the repository
- Create your feature branch (
git checkout -b feature/my-feature) - Run tests and linting:
npm test npm run lint npm run typecheck - Commit your changes (
git commit -m 'Add new feature') - Push to the branch (
git push origin feature/my-feature) - Open a Pull Request
📄 License
MIT © 2026 Bhagwat018
