Library / SDK
dvtng/react-loading-skeleton avatar
dvtng/react-loading-skeleton

Skeleton screens that measure themselves instead of being measured

Create skeleton screens that automatically adapt to your app!

4,205 stars162 forksTypeScriptMIT

At a glance

What is it?
The component is short, so the substance sits in the details: a count prop that accepts decimals, a height default that resolves to your own font size, an inline prop that changes the DOM rather than the CSS, a stylesheet you have to import yourself, and a build that runs tsc and rollup while the tests run under vitest.
Who is it for?
Use this when your loading state lives inside components that already have typography, because that is the assumption the whole design rests on: height falls back to the font size, so a skeleton matches the text it stands in for. Three things to check before you commit to it.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Activity is slowing. The repository last received commits 7 months ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 5, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Install is two commands, and the stylesheet is a second import

The installation section offers a Yarn and an npm line and nothing else:

bash
yarn add react-loading-skeleton
npm install react-loading-skeleton

The stylesheet is not part of that. The basic usage snippet imports the component and then imports the CSS as a second statement:

tsx
import Skeleton from 'react-loading-skeleton'
import 'react-loading-skeleton/dist/skeleton.css'

<Skeleton /> // Simple, single-line loading skeleton
<Skeleton count={5} /> // Five-line loading skeleton

The package manifest backs that up rather than hiding it. It declares `"sideEffects": ["**/*.css"]`, so bundlers are told not to drop the CSS import, and it publishes the stylesheet as its own export entry:

json
"exports": {
  ".": {
    "types": "./dist/index.d.ts",
    "require": "./dist/index.cjs",
    "import": "./dist/index.js"
  },
  "./dist/skeleton.css": "./dist/skeleton.css"
},

A consumer who skips that second import still gets the component, just without the animation the class names refer to.

count takes a decimal, and the last line comes out half width

The `count` prop defaults to 1 and sets how many skeleton lines render. Its documented quirk is that it does not have to be a whole number: a value like 3.5 produces three full skeletons and one half-width skeleton, which is a deliberate way to make a text block end mid-line the way a paragraph does.

The design argument for the whole component is that you place it where the content will be rather than building a separate screen for it:

tsx
function BlogPost(props) {
  return (
    <div>
      <h1>{props.title || <Skeleton />}</h1>
      {props.body || <Skeleton count={10} />}
    </div>
  );
}

The heading and the body get correctly sized skeletons with no configuration, because the component measures what is already on the page. The stated alternative is the one this library argues against: a dedicated skeleton screen that a developer has to tune by hand to match the font size, line height and margins of the real content, and to retune whenever those change.

height falls back to the font size, which is the whole trick

Almost every prop in the shared table has a literal default: `baseColor` is `#ebebeb`, `highlightColor` is `#f5f5f5`, `width` is `100%`, `borderRadius` is `0.25rem`, `duration` is `1.5` seconds. `height` is the exception, and its default is written as the font size rather than a number. That single row is what lets the component match a heading without anyone passing a height.

`circle` is the shorthand for a shape rather than a size: it sets border-radius to `50%` and defaults to false. `className` is added alongside the default class `react-loading-skeleton`, while `containerClassName` targets the span that wraps the individual elements, so there are two levels to style.

The `style` prop is labelled an escape hatch for advanced cases and explicitly not the preferred route, with one rule attached: props such as `width` and `borderRadius` take priority over the style object. In other words, an inline style cannot win against the matching prop, which is the opposite of how inline styles normally behave.

inline decides whether a break element exists in the DOM

By default a `<br />` is inserted after each skeleton so that each one occupies its own line, and when `inline` is true no line breaks are inserted at all. This is the one prop in the reference that changes the document structure rather than the appearance, and it defaults to false.

That matters for anything else counting lines. With `count={5}` you get five skeleton elements and five inserted break elements; with `inline` you get the five elements and nothing between them, so the container behaves like an inline run. Any CSS that assumed one skeleton per line has to account for the difference.

Testing gets a matching hook in the same table. `containerTestId` puts a `data-testid` attribute on the wrapping span rather than on the individual skeletons, and the documented way to use it is `screen.getByTestId` from React Testing Library. Since it sits on the container, one query selects the group, and a test that needs a single line still has to reach past it.

SkeletonTheme reaches every skeleton below it in the React tree

Individual skeletons take props, and a theme component sets the colours for everything below it in the React hierarchy:

tsx
import Skeleton, { SkeletonTheme } from 'react-loading-skeleton';

return (
  <SkeletonTheme baseColor="#202020" highlightColor="#444">
    <p>
      <Skeleton count={3} />
    </p>
  </SkeletonTheme>
);

The two colours it accepts are the background of the block and the highlight that sweeps across it, and the animation itself has two more knobs in the shared table: `duration` in seconds and `direction`, which takes either `ltr` or `rtl`. The table that documents both props is where the README stops, so anything beyond the animation direction, such as easing or a delay, is not specified in the prop reference at all.

Because the theme works through the React hierarchy rather than through CSS inheritance, a skeleton rendered in a portal or a subtree that skips the provider keeps its own defaults, not the theme's.

The build runs tsc and rollup, the tests run under vitest

The scripts in package.json describe four different tools doing four different jobs:

json
"scripts": {
  "build": "yarn clean && tsc && rollup -c",
  "clean": "rimraf dist",
  "lint": "eslint .",
  "test": "vitest"
}

Compilation goes through the TypeScript compiler and then rollup with a rollup TypeScript plugin, tests go through vitest with a vite.config.ts in the tree, and there is a .swcrc plus @swc/core among the development dependencies even though the build never calls it. Storybook has its own script pinned to port 8080 and a .storybook directory. Formatting and linting run through lint-staged, which is configured for TypeScript sources and for a short list of other globs.

The manifest is also a mix of old and new: it sets `"type": "module"` while shipping a CommonJS entry, and it repeats the entry points in both the exports map and the legacy main, module and types fields. The values agree, so nothing breaks, but there are two places to update whenever the output filenames change.

3.5.0 is what ships, and the branch has moved past it

package.json carries version 3.5.0, and that matches the newest release on record, dated 2024-09-23. Before it came v3.4.0 on 2024-02-15, whose release title is written without the v that its tag carries, and v3.3.1 on 2023-05-11. The master branch itself was pushed on 2026-03-05, so the source tree is newer than the published package by a long way, and the version string has not moved to show it. The repository is not archived and carries no support policy; CHANGELOG.md is the running record.

The README also points at two generations of documentation at once, with a link to what changed in version 3 and a separate link to the v2 documentation on its own branch, which tells you major version 2 is still being served as reference material.

One more dated oddity sits in the dependencies: the test tooling is pinned to a previous generation, with @testing-library/react at ^12.1.5 and @testing-library/jest-dom at ^5.16.5, while the Storybook packages are all at 7.0.7. The props reference tells you to use React Testing Library, which the repository itself develops against an old major of.

Editorial conclusion

Use this when your loading state lives inside components that already have typography, because that is the assumption the whole design rests on: height falls back to the font size, so a skeleton matches the text it stands in for. Three things to check before you commit to it. Import the stylesheet yourself, because it is a separate import and a separate export entry rather than something bundled into the component. Decide whether the inserted break elements are what you want, since inline is the prop that removes them. And pin your expectations to 3.5.0, the version in package.json and the newest release from 2024-09-23, since the master branch moved on 2026-03-05 without a version bump and the README points to two different major versions of documentation at once.

Frequently asked questions

What does skeleton loading mean in react-loading-skeleton?

It means rendering placeholder blocks in the place where content is still loading, sized from the typography already on the page. The component is meant to sit inside your own components rather than on a separate skeleton screen that has to be tuned by hand.

When should I use a skeleton loader instead of a spinner?

This project argues from its own principles rather than comparing the two: keeping styles in sync, letting a component represent every state including loading, and letting the title arrive before the body. No comparison against spinners appears in the documentation.

How do I install react-loading-skeleton?

Two commands are given, `yarn add react-loading-skeleton` and `npm install react-loading-skeleton`. The stylesheet is a separate statement afterwards: `import 'react-loading-skeleton/dist/skeleton.css'`.

Why is my react-loading-skeleton not styled?

The stylesheet ships as its own file and its own export entry, and the manifest lists CSS as a side effect so bundlers keep the import. Leave out the second import statement and the component renders without the classes the animation depends on.

Is react-loading-skeleton still maintained?

package.json carries 3.5.0, matching the newest release dated 2024-09-23, while the master branch was pushed on 2026-03-05. The repository is not archived, and no support policy or release schedule is stated anywhere in the tree.

Official sources

  1. dvtng/react-loading-skeleton on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dvtng-react-loading-skeleton.svg)](https://hysenlabs.com/projects/dvtng-react-loading-skeleton)