React Native Skeleton Loader Componen
Tags: javascript, reactnative, development, learning
Published: 2024-05-09 08:05:22

This is a reusable React Native component for rendering skeleton loaders with customizable dimensions and animations. Skeleton loaders are placeholders used to provide a better user experience while loading content in an application. The component supports both animated and static skeletons, and it can measure the dimensions of a child component to render skeletons with the same dimensions. This component can be useful for improving the perceived performance of your React Native application.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7bbksa2vj1pa8tiim5nj.png)



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mkmtk930zzcrm8vycc3p.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8fskspq67ohj8iu4yk2j.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9rga6hr45cw8gcyzgq3o.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pa2rst83go3zy8bz1urs.png)


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aqa8disdqwz3hmf3hp7l.png)

```
import { useState } from "react";
import { Dimensions, View, Text, Platform } from "react-native"
import LinearGradient from "react-native-linear-gradient";
import colors from "../utils/constant/colors";
import Animated, { useAnimatedProps, useFrameCallback, useSharedValue, withTiming } from "react-native-reanimated";

interface SkeltonContainerProps {
    child: any;
    childcount?: any;
    width?: any;
    height?: any;
}

interface SkeletonProps {
    width: number;
    height: number;
    marginTop?: number;
}

const AnimatedLinearGradient = Animated.createAnimatedComponent(LinearGradient);

const SkeletonAnimated = ({ width, height, marginTop }: SkeletonProps) => {
    const locationY = useSharedValue(0);

    useFrameCallback(() => {
        if (locationY.value == 1) locationY.value = withTiming(0, { duration: 1000 });
        if (locationY.value == 0) locationY.value = withTiming(1, { duration: 1000 });
    });

    const animatedProps = useAnimatedProps(() => {
        return { locations: [0, locationY.value] }
    })

    return (
        <AnimatedLinearGradient colors={[colors.LightGray, colors.White]} animatedProps={animatedProps} style={{ width: width, height: height, marginTop: marginTop }} />
    )
}

const SkeletonStatic = ({ width, height, marginTop }: SkeletonProps) => {
    return (
        <LinearGradient colors={[colors.LightGray, colors.White]} style={{ width: width, height: height, marginTop: marginTop }} />
    )
}

const Skeleton = ({ width = Dimensions.get('window').width, height = 200, marginTop = 0 }: SkeletonProps) => {
    return (
        Platform.OS == 'ios' ?
            <SkeletonAnimated width={width} height={height} marginTop={marginTop} /> :
            <SkeletonStatic width={width} height={height} marginTop={marginTop} />
    )
}


const SkeletonContainer = ({ child, childcount = 1, width = Dimensions.get('window').width, height = 64 }: SkeltonContainerProps) => {
    const [dimensions, setDimensions] = useState({ width: 0, height: 0 })
    return (
        child ?
            <View>
                <View style={{ opacity: 0 }} onLayout={(event) => setDimensions({ width: event.nativeEvent.layout.width, height: event.nativeEvent.layout.height })}>{child}</View>
                <Skeleton width={dimensions.width} height={dimensions.height} marginTop={-dimensions.height} />
                {[...Array(childcount - 1).keys()].map((index) => <Skeleton key={index} width={dimensions.width} height={dimensions.height} />)}
            </View> :
            [...Array(childcount).keys()].map((index) => <Skeleton key={index} width={width} height={height} />)
    )
}

export default SkeletonContainer;
```