DEV Community

loading...
Cover image for Adding a Loader Spinner in Gatsby site

Adding a Loader Spinner in Gatsby site

zakirsajib profile image Zakir Sajib ・3 min read

Usually we don't need a loader spinner in our Gatsby site as Gatsby sites usually fast (Super blazing fast as Gatsby.org claims). But in some situations we need it. For example if our users have poor WiFi or or slow 2G/3G connections then it would be a good idea to have loader spinner before contents load. I tested my few Gatsby sites in Regular 3G, 4G and WIFI it super fast but when I tested in slow 2G or DSL it takes time. That's the reality.

So I was wondering if I could show a some kind of loading spinner before the site or components are ready.

html.js

This is the file where we will use to load our spinner.

First step: Type the following command in your terminal:

cp .cache/default-html.js src/html.js

It will copy the default-html.js file and rename it as html.js and will place it in our src directory.

Alt Text

If you open the html.js file you may see something like this:

import React from "react"
import PropTypes from "prop-types"

export default function HTML(props) {
  return (
    <html {...props.htmlAttributes}>
      <head>
        <meta charSet="utf-8" />
        <meta httpEquiv="x-ua-compatible" content="ie=edge" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1, shrink-to-fit=no"
        />
        {props.headComponents}
      </head>
      <body {...props.bodyAttributes}>
        {props.preBodyComponents}
        <div
          key={`body`}
          id="___gatsby"
          dangerouslySetInnerHTML={{ __html: props.body }}
        />
        {props.postBodyComponents}
      </body>
    </html>
  )
}

HTML.propTypes = {
  htmlAttributes: PropTypes.object,
  headComponents: PropTypes.array,
  bodyAttributes: PropTypes.object,
  preBodyComponents: PropTypes.array,
  body: PropTypes.string,
  postBodyComponents: PropTypes.array,
}

Now we have to add our own image (either gif or SVG) and add few lines of css and regular javascript codes:

import LoaderSVG from './img/loader.svg'
<div
    key={`loader`}
              id="___loader"
              style={{
                alignItems: "center",
                backgroundColor: "#F2F2F2",
                display: "flex",
                justifyContent: "center",
                position: "absolute",
                left: 0,
                top: 0,
                right: 0,
                bottom: 0,
                zIndex: 100,
              }}
           >
              <img 
                src={LoaderSVG} 
                alt="loading spinner" 
                width="150" 
                height="150"
              />
        </div>

Complete code something like this:

import React from "react"
import PropTypes from "prop-types"
import LoaderSVG from './img/loader.svg'

export default function HTML(props) {
  return (
    <html {...props.htmlAttributes}>
      <head>
        <meta charSet="utf-8" />
        <meta httpEquiv="x-ua-compatible" content="ie=edge" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1, shrink-to-fit=no"
        />
        {props.headComponents}
      </head>
      <body {...props.bodyAttributes}>
        {props.preBodyComponents}
        <div
              key={`loader`}
              id="___loader"
              style={{
                alignItems: "center",
                backgroundColor: "#F2F2F2",
                display: "flex",
                justifyContent: "center",
                position: "absolute",
                left: 0,
                top: 0,
                right: 0,
                bottom: 0,
                zIndex: 100,
              }}
           >
           <img 
              src={LoaderSVG} 
              alt="loading spinner" 
              width="150" 
              height="150"
           />
        </div>
        <div
          key={`body`}
          id="___gatsby"
          dangerouslySetInnerHTML={{ __html: props.body }}
        />
        {props.postBodyComponents}
      </body>
    </html>
  )
}

HTML.propTypes = {
  htmlAttributes: PropTypes.object,
  headComponents: PropTypes.array,
  bodyAttributes: PropTypes.object,
  preBodyComponents: PropTypes.array,
  body: PropTypes.string,
  postBodyComponents: PropTypes.array,
}

Now we will add our JavaScript code in gatsby-browser.js.

Add this following code:

export const onInitialClientRender = () => {
    setTimeout(function() {
        document.getElementById("___loader").style.display = "none"
    }, 1000)
}

Save it.

Now we can restart our Gatsby. And run the site. You may not notice the loader spinner if you have fast connection but if you can change your connection to DSL or slow 2G and refresh the browser then you will see the loader.

Alt Text
Alt Text

Reference:

Live site:
Nirvana

Discussion (6)

pic
Editor guide
Collapse
heytulsiprasad profile image
Tulsi Prasad

Hey Zakir, thanks for writing this post! It really works and is awesome.

However, do you have any ideas about how can we make the loader screen go away with a fade animation and not statically? I tried adding transition: opacity 0.5s ease into styles and this to gatsby-brower:

export const onInitialClientRender = () => {
  setTimeout(() => {
    setTimeout(() => {
      document.getElementById("___loader").style.opacity = "0"
    }, 500)
    document.getElementById("___loader").style.display = "none"
  }, 1000)
}
Enter fullscreen mode Exit fullscreen mode

But it still doesn't work. Can you enlighten me on why this is happening?

Collapse
blainesensei profile image
Blaine

Thats because the display property does not support css animations.
You can try, jquery.fadeout or use style.opacity = '0' and style.visibility = 'hidden' with transition : all 0.5s ease

Collapse
simonjoom profile image
simz

Thanks for the post, i began in a same way and wanted to confirm with others.
You can add to put the style of the loader directly in html.js too, to avoid gatsby load others css and script before the one of the loader

<style dangerouslySetInnerHTML={{ __html: `.preloader-wrapper ;.. `}}>
Enter fullscreen mode Exit fullscreen mode


`

Collapse
binothird profile image
binothird

Thanks for the very helpful solution Zakir!

Based from what I understand, the "onInitialClientRender" Gatsby lifecycle fires after the initial render of the Gatsby App on the client.

To achieve the same result, since all my data is statically generated, here is my JavaScript code.

exports.onInitialClientRender = () => {
document.getElementById("___loader").style.display = "none"
}

There is no set timeout because the only moment that the loader will disappear is when the initial render of the Gatsby App is finished.

Is there any subsequent issue on my code or did I get and understand it correctly.

Thanks a lot!

Collapse
justinledelson profile image
justinledelson

Is there anything else required in gatsby-browser.js? My file was empty.

Collapse
usmanweb profile image
Muhammad Usman

Thanks for this post 👍.