We’ve all been there.
You click a button expecting the next page to instantly fill your screen with content, only to be met with a dreaded loading icon — seconds become minutes as you frantically reload the page in a bid to get what you asked for.
My point being, nobody likes to wait…
In an aim to mitigate this torture emerged a clever visual illusion that seems to reduce the perceived loading time. Introducing skeleton loading pages — which are essentially a blank version of the page where information is gradually loaded.
Skeleton loading screens are widely used across pretty much all your favourite websites:
The user is instantly greeted with the skeleton as it pulses with grey shapes which mimic the page layout. The content requested replaces the placeholders as they become available, until the skeleton’s job is done — creating the illusion of an instant transition.
Skeleton pages in React
There are some packages out there that you can use, for example React Loading Skeleton. However, this will add dependencies to your codebase and are not as customisable as if you were to develop your own. The rest of this guide will show you how to implement skeleton loading with styled-components, but the same principles can be applied with vanilla CSS.
The approach we went with for implementing skeleton loading pages is not to create a separate, dedicated skeleton page but rather a built-in ‘skeleton state’ of a component. This is a more robust and scalable solution that will allow for more flexible loading patterns across different pages.
Let’s start by making a simple skeleton component that can be reused across pages. First, let’s add some styling for the pulse of the skeleton. The reason we separate the pulse out, is so I can use it for more shapes (e.g. circles, rectangles, etc.) if needed.
Next, add some styling for the skeleton line. Skeleton components become more robust if they can inherit some styling from their parent element. A really effective trick is to use the line height of parent element to set the size of the skeleton component — this is done by using the ::before selector and filling it with empty content (a string, “\00a0”).
As you can see above, instead of having to create three skeleton lines with different sizes, the same skeleton line inherits three different sizes from the respective parent elements.
We can then export our skeleton line to be used across pages.
Now we have our skeleton, we need to implement it. As mentioned, the approach we use is to create a ‘skeleton state’ for the desired component. The implementation will depend on what data your page is using but we want the skeleton to render when the data we need is undefined and render the page once the data has been loaded. For example:
You could also use a ternary but I think it’s clearer to use if statements when components become more complex. This method allows you to import your skeleton to any component and it seamlessly build entire skeleton pages quickly.
If you want to build a more customisable skeleton, you can allow it to take in props that change the styling of the skeleton. For example, these skeletons work well on white/neutral backgrounds but not so well when the background is darker. I want to be able to make my skeleton line translucent for some pages:
So, if I want to make the skeleton line translucent, all I have to do in the SkeletonLine element is:
Skeleton loading pages have become the go-to solution for all your loading transition needs. Some research validates the idea that the perceived loading time is shorter for skeletons as opposed to spinners, for example. But before you dive in to skeletons, there are some caveats:
You are increasing the amount of code you have — your components will need an additional layer of logic to handle what should be rendered and when. This will add a significant amount of code to what was originally a simple React component and could make things harder to debug. Also, as mentioned above, if you decide to use a package, you are adding more dependencies to your codebase, which increases security risks and is less customisable than implementing a simple solution yourself.
Changes to components inevitably mean changes to the skeleton. The method outlined above is robust, however, if you change the layout or add features, you also need to make sure the skeletons are changed to reflect that.
Although some research validates skeleton loading pages, there has also been skepticism around their effectiveness; citing small sample size and lack of variation as concerns about studies conducted — there have even been conflicting reports stating that skeleton pages may perform worse for perceived loading time.
Regardless, the use of skeleton loading pages is everywhere and is quickly becoming the expectation of what the ‘loading experience’ is. As you can see, it’s not hard to do, so give it a go, experiment with different approaches and decide if they are just a trend or if they really do work…
Please respond below, if you know of any better ways to implement skeleton loaders.