Creating Beautiful Skeleton Loaders with React and TailwindCSS
Using TailwindCSS's built-in animations and some helpful tricks, you can create skeleton loading states that reduce users' perception of your app's loading time.
What is a Skeleton?
Skeletons are low-fidelity representations of what a page or component will look like when it’s done loading. It’s a tactic used by some of the most popular websites and apps out there, such as YouTube, Facebook, Twitter, and LinkedIn.
As content loads, it should be progressively shown rather than waiting for the full page to be ready. This is why, when done correctly, skeletons can make a website seem so fast. My favorite example of this is Vercel’s dashboard:
Why Should You Use Skeletons?
- Faster perceived loading times - If your data loads quickly, research has shown that users typically perceive loading times to be faster when apps use skeletons instead of loading spinners. They can make users focus on the content instead of the amount of time they’ve spent waiting.
- Optimizing CLS - Cumulative layout shift is the measure of how “stable” your page is. If you have content popping in as it loads without defining its size, it will increase your CLS. Implementing a skeleton forces you to estimate the sizes of your components, and if implemented correctly, they can help you reduce CLS on your website.
Why Shouldn’t You Use Skeletons?
- If you are fetching data on the server, skeletons typically aren’t necessary — unless you’re using something like an instant loading state while your app is rendering on the server.
- If your data takes a long time to load, skeletons may actually make load times seem longer to your users.
Implementing a Skeleton Loader in React
The Skeleton Component
When I wanted to implement skeleton loaders in my todo list app, I originally created my own component. With Tailwind, the process is extremely simple.
If you want the size to be customizable, you can let the component accept a className
:
clsx
is a utility for combining class names. You can also just concatenate the className
prop value to the div’s className
, or use a tool called tailwind-merge
to avoid style conflicts.
The key here is the animate-pulse
class. If we look in the generated CSS, we see that it’s a very simple animation:
You can also use the shadcn/ui
component which essentially does the same thing.
Some Tips For Creating Layouts
Now that you have a basic composable, it’s time to re-create your layouts.
Sizing your skeleton components properly is the key to making a good-looking loading state. In your fully-loaded state, open the developer tools and inspect the element you would like to make a skeleton for. Your browser should give you the dimensions of its bounding box:
Then, you can assign a width and height to your skeleton element to match:
For most text-based elements, the text won’t take up the full box, so you can replace some of the height with vertical margin:
Now, combining these elements into a layout becomes very simple. Here’s an example from my todo list app using shadcn/ui:
And a more complicated example:
In this example, I wanted some tasks to have due dates and others to have descriptions. The simplest way to make an even-looking distribution was to create a formula with the row and column indexes (i
and j
):
Also, the array that I iterate over indicates the amount of cards in each column:
Finally, for more variability, the first two cards in each column had a checked checkbox:
Altogether, this produces a beautiful skeleton that gives users an idea of what the content will look like when it’s done loading:
By the way, if you want to use this app, check it out here! It’s open-sourced on GitHub.