A React Hook to prevent flickering spinners

Ward Poel

One of the essential things in building modern web apps is to provide a good user experience. Think of showing a loading indicator when waiting, for example, a spinner when you’re submitting a form. These fancy animations are excellent if they are not flickering before your eyes. In this blog, you will find a practical React Hook to prevent loading indicators from flickering.

The problem

First, let’s start with a basic example where your loading indicator could flicker based on your internet speed. In this example, we have a list of books, and we want to add a new book to our must-read list.

Example of a flickering loading indicator

I think showing a loading indicator on your application is a great plus for UX. This way, users know the application is busy doing stuff in the background, and they are not stuck on a white screen. But what if your backend response is super fast, and your user gets a blink of a loading indicator? In that case, showing a loading indicator can be pretty annoying.

The code snippet above will always show a spinner, even when the data is fetched within 0.2 seconds. Of course, you need to display a spinner when data fetching takes more than 0.2 seconds. However, in my opinion, when data fetching is done within 0.2 seconds, a spinner is not needed.

The solution

The solution is simple: only show loading indicators when your async tasks are taking too long. But what is too long? When your server responds within 200ms, it is acceptable not showing a loading indicator (of course, this is a personal preference). With the useLoadingState React Hook I made, it’s possible to change the times 😉.

Let’s upgrade our previous example to work with our new hook:

Let’s take a closer look at the hook:

  • addBook is an async function returned by the hook.
  • runningAddBook is a boolean that will turn true when we call addBook , this boolean is used to disable the onClick of the Button. • This is not a native HTML attribute but a custom one that prevents clicking the Button.
  • pendingAddBook • is a boolean that will become true after a delay we can provide in the options object. The default delay is 200ms. After 200ms, the boolean will become true, and we will disable the Button and show a spinner inside of the Button based on this boolean. When this boolean changes to true, it will at least stay true for minBusyMs variable we also can pass in the options object. The default value for minBusyMs is 500ms.
  • callback is your async function which will execute when calling addBook. In this case, this function will make a POST request to our books endpoint.
  • options is an object you can pass through. Example: { delayMs: 400ms, minBusyMs: 600ms }, • if we would pass this options object, the spinner will show after 400ms if the request is not finished yet. And when the spinner shows, it will stay for at least 600ms.

Would you like to see a live example? Check it out here: https://codesandbox.io/s/use-loading-state-2pv8m1


The UX in today’s web apps improved massively in the last couple of years. However, there is still room for improvement 😉.

More insights

Collaborate with Us

Don't miss out on the opportunity to partner with Wheelhouse. Together, we can bring your project to the next level.