SoFunction
Updated on 2025-04-07

React implements the skeleton screen sample code

background

Recently, I used the skeleton screen in the project. Since the project uses the antd component library, I took it for granted.Skeleton Components

However, when using it, after writing the business code, you still need to deal with the code related to the skeleton screen in a targeted manner.

Purpose

Reduce the development time of the skeleton screen, and it is best to automatically generate the skeleton screen of the related pages.

Technology stack

react @16.8 >=

sass

accomplish

To develop a relatively automated skeleton screen component, you only need to introduce this component into the business component that uses the skeleton screen, and then the corresponding skeleton screen structure will be automatically generated based on the current dom structure.

Here, the blogger has developed a component by himself, which is currently a perfect match for the scenario I use, and will continue to update and iterate according to the business in the future. However, everyone is also welcome to help improve it.

Sample code

The method of using components for the time being is as follows. Of course, if you are interested, you can get the source code and modify it yourself to the desired method.

import { useState, Fragment, useEffect } from 'react';
import { AutoSkeleton, Button } from 'gyp-gao-ui';

export default () => {
  const [isShow, setIsShow] = useState(true);
  const [loading, setLoading] = useState(false);

  const handleFresh = () => {
    setLoading(true);
    setTimeout(() => {
      setLoading(false);
    }, 2000);
  };

  /** Simulation request */
  useEffect(() => {
    handleFresh();
  }, []);

  return (
    <>
      <Button text="Click to refresh" onClick={handleFresh}></Button>

      <h1>You can also try to update yourselfdomelement,See if the generated skeleton screen is desirable</h1>
      <div>
        {isShow && (
          <Fragment>
            <p>This is a paragraph</p>
            <p>This is a paragraph</p>
            <img src="/rmsportal/" />
            <div
              style={{
                marginTop: '20px',
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'space-between',
              }}
            >
              <button>show</button>
              <p>This is a paragraph</p>
              <img src="/rmsportal/" />
            </div>
            <div>
              <button>show</button>
              <p>This is a paragraph</p>
              <img src="/rmsportal/" />

              <div>
                <button>show</button>
                <p>This is a paragraph</p>
                <img src="/rmsportal/" />
                <div
                  style={{
                    marginTop: '20px',
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'space-between',
                  }}
                >
                  <button>show</button>
                  <p>This is a paragraph</p>
                  <img src="/rmsportal/" />
                </div>
              </div>
            </div>
          </Fragment>
        )}
        <AutoSkeleton
          loading={loading}
          onLoadingSuccess={() => {
            setIsShow(true);
          }}
          onComplete={() => {
            setIsShow(false);
          }}
        />
      </div>
    </>
  );
};

Core idea

In a small independent component, the corresponding skeleton screen is generated. From the use, we can see that when the blogger is using it, the AutoSkeleton component and the content are isolated through a Fragment component and exist together in a container (div);

Extract the components in the skeleton screen and subdivide the particles. Judging from the commonly used skeleton screens on the market, they are nothing more than two types of text and pictures, and then a series of evolutions are carried out to divide them into different skeleton screen widgets. Here the blogger only divides them into two types of skeleton screen widgets;

The harsh html tags are divided into the above two widgets, and the classification code is

/** is a combination of tag types for text type */
export const textTypes = ['p', 'span', 'a', 'button', 'input', 'textarea', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'div', 'li', 'ul', 'ol', 'pre', 'code', 'blockquote', 'cite','strong', 'em','mark', 'del', 'ins','sub','sup','small', 'big']

​​​​​​​/** combination of tag types for image type */
export const imageTypes = ['img','svg', 'picture', 'video', 'audio']

Generate a skeleton screen, traversing the content nodes in the container. Different categories and render different widgets. What needs to be handled specifically here is that if the current component is a div and children exist, then the widget will continue to be generated.

Core code

Generate skeleton screen components

import React, { useEffect, useRef, useState } from 'react';
import { comp_className } from '../constants';
import './';
import { Image, Text, imageTypes, textTypes } from './skeleton';
import { nanoid } from 'nanoid';

export interface IAutoSkeletonProps {
  /** Generate the skeleton screen complete */
  onComplete?: () => void;
  /**
    * loading
    * @default true
    */
  loading: boolean;
  /** Loading complete callback */
  onLoadingSuccess?: () => void;
}

function AutoSkeleton(props: IAutoSkeletonProps) {
  const { onComplete, loading = true, onLoadingSuccess } = props;
  const [showMenu, setShowMenu] = useState(false);
  const [currentPoint, setCurrentPoint] = useState<{ x: number; y: number }>({
    x: 0,
    y: 0,
  });
  const currentRef = useRef<HTMLDivElement>(null);
  const [skeleton, setSkeleton] = useState<any>();
  const genSkeleton = () => {
    if (!) return;
    const parent = ;
    if (!parent) return;
    /** Elements other than skeleton screen content */
    const targetElements = ().filter(
      (o) => !(comp_className + 'auto-skeleton'),
    );

    const getSkeletonSon = (elements: Element[], parent: Element) => {
      const child = elements
        .map((k) => {
          if ( > 0 && () === 'div') {
            return getSkeletonSon((), k);
          }
          if ((())) {
            return <Image key={nanoid()} />;
          }
          if ((())) {
            return <Text key={nanoid()} />;
          }

          return null;
        })
        .filter((k) => k !== null);
      const style = getComputedStyle(parent);
      return (
        <div
          key={nanoid()}
          style={{
            display: 'flex',
            width: ,
            flexDirection:
               &&  === 'flex'
                ? ( as any)
                : 'column',
            justifyContent: ,
            alignItems: ,
            gap:  === 'normal' ? '12px' : ,
          }}
        >
          {child}
        </div>
      );
    };

    const getSkeletonChild = (elements: Element[]) => {
      return elements
        .map((o) => {
          if ( > 0 && () === 'div') {
            return getSkeletonSon((), o);
          }
          if ((())) {
            return <Image key={nanoid()} />;
          }
          if ((())) {
            return <Text key={nanoid()} />;
          }

          return null;
        })
        .filter((o) => o !== null);
    };

    const skeletonContent = getSkeletonChild(targetElements);

    setSkeleton(skeletonContent);
    setTimeout(() => {
      onComplete && onComplete();
      setShowMenu(false);
    }, 0);
  };

  useEffect(() => {
    if (loading) {
      genSkeleton();
    } else {
      onLoadingSuccess && onLoadingSuccess();
    }
  }, [loading]);

  return (
    loading && (
      <div
        className={`${comp_className}auto-skeleton`}
        ref={currentRef}
      >
        {skeleton}
      </div>
    )
  );
}

export default AutoSkeleton;

When it comes to styles, the blogger here also posted the code, and the style of the skeleton screen is still quite complicated.

.xxx-auto-skeleton {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 12px;

  &-image {
    flex: 0 0 auto; // Prevent it from being squeezed    width: 80px;
    height: 80px;
    background: linear-gradient(
      90deg,
      rgba(190, 190, 190, 0.2) 25%,
      rgba(129, 129, 129, 0.24) 37%,
      rgba(190, 190, 190, 0.2) 63%
    );
    border-radius: 8px;
    animation: xxx-skeleton-loading 1.4s ease infinite;
    background-size: 200% 100%;
    background-position: 100% 50%;
    background-position-x: 180%;
  }

  &-text {
    width: 100%;
    height: 32px;
    border-radius: 4px;

    background: linear-gradient(
      90deg,
      rgba(190, 190, 190, 0.2) 25%,
      rgba(129, 129, 129, 0.24) 37%,
      rgba(190, 190, 190, 0.2) 63%
    );
    animation: xxx-skeleton-loading 1.4s ease-in-out infinite;
    background-size: 200% 100%;
    background-position: 100% 50%;
    background-position-x: 180%;
  }

  @keyframes xxx-skeleton-loading {
    to {
        background-position-x: -20%;
      }
  }
}

The code of widgets is relatively simple, just for subsequent maintenance and is extracted. Here we take Text as an example

import React from 'react';
import { comp_className } from '../../constants';
import '../';

​​​​​​​export default function TextSkeleton() {
    return (
        <div className={`${comp_className}auto-skeleton-text`}></div>
    )
}

Written at the end

If you follow the above carefully, you should be able to have the same effect as the blogger!

However, I am afraid that some friends will be troublesome, so the blogger also thoughtfully released my component library and can download it directly in the npm repository

npm i -S gyp-gao-ui

This is the end of this article about the sample code of React implementing the skeleton screen. For more related React skeleton screen content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!