Skeleton Loading with Suspense in Next.js 13

My strategy for handling skeleton loading with Suspense.

Published 29 December 2022 - Updated 29 December 2022 - by Alvar Lagerlöf

With Next.js 13 released recently, I've started thinking a lot more about how loading happens and is communicated to the user. I ended up with something like this:

Website, projects page. A grid of projects are loading, indicated by a skeleton UI representing 4 loading projects.

Suddenly, with Suspense, it is way more scaleable and convenient to do something pretty yet simple. However, in my adventures, I ran into some gotchas along the way and found some strategies to keep it all tidy. Let's dive in!

Hold up, Suspense?

No worries. Suspense is a special new component that handles something loading. While that is happening, it renders a fallback. Let's look at a minimal example:

This example is not complete, but we can start to see a structure. While <Posts /> are loading, show <p>Loading...</p>.

Let's take a look at <Posts />. There are two ways to do it, depending on if it's rendered on the server or the client.

These both are very similar, and also behave similarly in terms of Suspense. There are big differences underneath, but we don't need to consider them for our skeletons.

Let's make some Skeletons!

I use Tailwind, and there are lots of useful utilities to make making skeletons simpler. Even so, I found making a <Skeleton /> component to be useful. Mine looks something like this:

You get a few nice things from doing this:

  • The skeleton has a slight pulsating animation, to indicate that there is activity happening.
  • The animation does not show if the user prefers reduced motion (via motion-safe).
  • There's a default design with slightly rounded corners and a background color.

Now let's take this, and try to make a component indicating loading for our <Posts />.

This gives us something like this:

Skeleton UI showing three items loading. Each has a title and a description.

We can now use this component in our <Suspense> like this.

That works! We can stop here, but there is an opportunity for a different structure.

What if each component provides their own skeleton

What if each Post in our <Posts /> list provided its own Skeleton? Let's take a look at how that plays out:

This is pretty much what I landed in. How much to separate the components into different files is up to you, but the idea of a component exporting both its complete and loading state was a powerful idea for composing skeleton UIs on my site.

If you've tried it yourself, I'd love to hear your take on skeleton UIs with Suspense.