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.
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.
Reference:
- https://www.gatsbyjs.org/docs/custom-html/
- https://www.gatsbyjs.org/docs/browser-apis/#onInitialClientRender
Live site:
Nirvana
Top comments (6)
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:But it still doesn't work. Can you enlighten me on why this is happening?
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
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
`
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!
Is there anything else required in gatsby-browser.js? My file was empty.
Thanks for this post 👍.