DEV Community

Cover image for React Native Skeleton Loader Componen
Akash Yadav
Akash Yadav

Posted on

React Native Skeleton Loader Componen

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

Image description

Image description

Image description

Image description

Image description

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;
Enter fullscreen mode Exit fullscreen mode

Top comments (0)