Introduction
Gatsby sites are fast by design (as the official website claims) and personally I can only agree with that statement. However, you might have encountered a situation before where you wanted to briefly display a page loader (or as I call it, preloader) while some parts of the website are still loading, whatever the reason may be.
In this post we will go through steps to create a simple customizable preloader that will overlay the page content and fade out once the document is ready. In the end we will also add fallback support for users that have JS disabled in their browsers. The complete code can be found in the summary section. Let's get started!
Implementation
Creating preloader component
The visual portion of the preloader consists of a parent element and custom inner content. For an inspiration, I will be providing a simple logo placeholder and animation, however feel free to customize or replace it to better fit your own needs.
Since Gatsby's provides a set of server rendering APIs, we will utilize the gatsby-ssr.js
file and its functions. First we create the JSX component with a parent div
element and give it an ID of preloader
. Inside of it, we define our custom content, in this case a logo image and a animation element.
In order to add this component to the final HTML, we pass it through the setPreBodyComponents
function on the Gatsby's onRenderBody
event, which is called during the server side rendering. As the last step we import React for it to be complete:
gatsby-ssr.js
const React = require("react")
exports.onRenderBody = ({
setPreBodyComponents
}) => {
setPreBodyComponents([
<div id="preloader">
{/* Optional: */}
<img src="/images/logo.png" alt="logo" style={{"height": "calc(3.23625vw + 77.86408px)"}} />
<div className="preloader_animation"></div>
</div>
])
}
Next, we will be adding styles for this component. Again, the inner content styling is up to you and my demo styles can be found at the bottom of the code block:
src/styles/preloader.scss
body {
#preloader {
position: fixed;
display: none;
top: 0;
left: -10%;
right: -10%;
height: 0;
margin-left: calc(100vw - 100%);
overflow-x: hidden;
}
&.preloader_active {
height: 100vh;
overflow-y: hidden;
#preloader {
height: auto;
bottom: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #27135c;
z-index: 9999;
display: flex;
overflow: hidden;
}
}
&.preloader_ready {
height: auto;
overflow-y: auto;
#preloader {
animation: preloaderLeaving 0.5s forwards;
@keyframes preloaderLeaving {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
}
}
}
// Optional:
body {
#preloader {
img {
z-index: 120;
}
.preloader_animation {
position: absolute;
width: calc(3.23625vw + 77.86408px);
height: calc(3.23625vw + 77.86408px);
border: 5px solid #ffffff;
border-radius: 50%;
opacity: 0;
z-index: 110;
animation: preloaderAnimation 1.5s ease-out infinite 0s;
@keyframes preloaderAnimation {
0% {
transform: scale(.1);
opacity: 0.0;
}
50% {
opacity: 1;
}
100% {
transform: scale(1.2);
opacity: 0;
}
}
}
}
}
Let's not forget importing this file into the final HTML through the gatsby-browser.js
file:
gatsby-browser.js
import "./src/styles/preloader.scss"
Adding functionality
As you might have noticed in the stylesheet, we are defining a few currently unused classes such as preloader_active
and preloader_ready
. Each of these is bound to a stage in the preloader life cycle:
preloader_active
This is the starting class/stage. If the body
element contains this class, the preloader is visible and overlays the page content. To add this class, we will return to the gatsby-ssr.js
and add it like this:
gatsby-ssr.js
// ...
exports.onRenderBody = ({
// ...
setBodyAttributes
}) => {
// ...
setBodyAttributes({
className: "preloader_active"
})
}
preloader_ready
Once the document is ready, we can start fading out the preloader, thanks to the CSS animation in this class. We add the class to the body with a script that is waiting for the document state change. First, we have to create the given script:
static/scripts/preloader.js
var body = document.querySelector("body");
document.onreadystatechange = function () {
if (document.readyState === "complete") {
body.classList.add("preloader_ready");
setTimeout(function () {
body.classList.remove("preloader_active");
body.classList.remove("preloader_ready");
}, 500);
}
};
Then we insert the script into the final HTML, using the gatsby-ssr.js
file and the available API yet again:
gatsby-ssr.js
// ...
exports.onRenderBody = ({
// ...
setHeadComponents,
setPostBodyComponents
}) => {
setHeadComponents([
<link as="script" rel="preload" href="/scripts/preloader.js" />
])
// ...
setPostBodyComponents([
<script src="/scripts/preloader.js" />
])
}
We want this script to be available as soon as possible, which is the reason for pointing to it twice. You can read more about content preloading in MDN Web Docs' guide.
Finishing with noscript support
Even though we are building a website based on JavaScript, we still want to support users who prefer to have JS disabled in their browsers. Since the preloader is dependent on a script, it would stay visible forever, preventing the user to see any content. We can simply include a separate stylesheet inside a noscript
tag in the head
of the page (explicitly allowed in HTML5), which hides the preloader:
static/styles/noscript.css
body.preloader_active {
height: auto;
overflow-y: auto;
}
body.preloader_active #preloader {
display: none;
}
gatsby-ssr.js
// ...
exports.onRenderBody = ({
// ..
}) => {
setHeadComponents([
// ...
<noscript>
<link rel="stylesheet" href="/styles/noscript.css" />
</noscript>
])
// ...
}
Summary
We have successfully added a preloader to our Gatsby site. Upon entering the site, the preloader will be overlaying the content until the document is ready, and then it will fade out. If the user has disabled JS in their browser, the preloader will remain hidden through a stylesheet.
Below you can find all files that we have added or modified:
Collapsible
gatsby-ssr.js
const React = require("react")
exports.onRenderBody = ({
setHeadComponents,
setPreBodyComponents,
setBodyAttributes,
setPostBodyComponents
}) => {
setHeadComponents([
<link as="script" rel="preload" href="/scripts/preloader.js" />,
<noscript>
<link rel="stylesheet" href="/styles/noscript.css" />
</noscript>
])
setPreBodyComponents([
<div id="preloader">
{/* Optional: */}
<img src="/images/logo.png" alt="logo" style={{"height": "calc(3.23625vw + 77.86408px)"}} />
<div className="preloader_animation"></div>
</div>
])
setBodyAttributes({
className: "preloader_active"
})
setPostBodyComponents([
<script src="/scripts/preloader.js" />
])
}
gatsby-browser.js
import "./src/styles/preloader.scss"
src/styles/preloader.scss
body {
#preloader {
position: fixed;
display: none;
top: 0;
left: -10%;
right: -10%;
height: 0;
margin-left: calc(100vw - 100%);
overflow-x: hidden;
}
&.preloader_active {
height: 100vh;
overflow-y: hidden;
#preloader {
height: auto;
bottom: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #27135c;
z-index: 9999;
display: flex;
overflow: hidden;
}
}
&.preloader_ready {
height: auto;
overflow-y: auto;
#preloader {
animation: preloaderLeaving 0.5s forwards;
@keyframes preloaderLeaving {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
}
}
}
// Optional:
body {
#preloader {
img {
z-index: 120;
}
.preloader_animation {
position: absolute;
width: calc(3.23625vw + 77.86408px);
height: calc(3.23625vw + 77.86408px);
border: 5px solid #ffffff;
border-radius: 50%;
opacity: 0;
z-index: 110;
animation: preloaderAnimation 1.5s ease-out infinite 0s;
@keyframes preloaderAnimation {
0% {
transform: scale(.1);
opacity: 0.0;
}
50% {
opacity: 1;
}
100% {
transform: scale(1.2);
opacity: 0;
}
}
}
}
}
static/scripts/preloader.js
var body = document.querySelector("body");
document.onreadystatechange = function () {
if (document.readyState === "complete") {
body.classList.add("preloader_ready");
setTimeout(function () {
body.classList.remove("preloader_active");
body.classList.remove("preloader_ready");
}, 500);
}
};
static/styles/noscript.css
body.preloader_active {
height: auto;
overflow-y: auto;
}
body.preloader_active #preloader {
display: none;
}
static/images/logo.png
Top comments (1)
how can i add preloader at everytime i change page in this website?