It’s pretty common to see a loading state on sites these days, particularly as progressive web apps and reactive sites are on the rise. It’s one way to improve “perceived” performance — that is, making it feel as though the site is loading faster than it actually is.
There’s no shortage of ways to make a loader — all it takes is a quick search on CodePen to see oodles of examples, from animated GIFs to complex animations. While it’s tempting to build the fanciest of the fancy loaders, we can actually do a pretty darn good job with only a minimal amount of CSS and JavaScript.
Here’s an example we can make together.
See the Pen
Preloader with JavaScript FadeOut by Maks Akymenko (@maximakymenko)
on CodePen.
SVG and CSS is all we need for the spinner
I’m going to assume that you’ve already created a project, so we’ll jump right in and start with the spinner — or “pre-loader” as it is also called.
SVG is a great option for a spinner. It’s scalable and implementing it is as easy as an image tag. We could make one from scratch, say in an image editor like Illustrator, Sketch or Figma. For this example, I’m just going to grab this one from loading.io, which is a nice resource to make and customize different loaders.
Now that we have an SVG for the visual, we can drop it into HTML:
<div class="preloader">
<img src="spinner.svg" alt="spinner">
</div>
We’re using .preloader
as a wrapper, mostly because it helps us position the image on the page, but also because it will help us hide and reveal the page content while the loader works.
Let’s style it up:
.preloader {
align-items: center;
background: rgb(23, 22, 22);
display: flex;
height: 100vh;
justify-content: center;
left: 0;
position: fixed;
top: 0;
transition: opacity 0.3s linear;
width: 100%;
z-index: 9999;
}
This is doing a few things that are more than cosmetic:
- We’re displaying the loader directly in the center of the screen, using flexbox properties and values.
- We’ve made the element take up the entire width and height of the screen and given it a black (well, actually a really, really dark gray) background. That means anything behind it (like the page content) is completely hidden. If our page was a different background color (e.g. white), then we would adjust the loader’s background color accordingly.
- The position is fixed so scrolling won’t affect it’s location on the page. Plus, the
z-index
is high enough that not other element should stack on top of it and block it from view.
This is what we should see so far when opening it up in the browser:

Some light JavaScript handles the hiding
We’ve got a fancy spinner that covers the entire page, whether we’re viewing this on small or large screens. No we can write some logic to make it fade out after a certain amount of time. That’ll take a small dose of JavaScript.
First, let’s select the .preloader
element we just styled up:
const preloader = document.querySelector('.preloader');
It would actually be a lot easier to add a class to the loader that sets its opacity to zero. However, it wouldn’t be as smooth as what we’re doing here, which is using a tiny helping of JavaScript to create a fadeOut
function.
const fadeEffect = setInterval(() => {
// if we don't set opacity 1 in CSS, then
// it will be equaled to "" -- that's why
// we check it, and if so, set opacity to 1
if (!preloader.style.opacity) {
preloader.style.opacity = 1;
}
if (preloader.style.opacity > 0) {
preloader.style.opacity -= 0.1;
} else {
clearInterval(fadeEffect);
}
}, 100);
💡 jQuery has a function that does this right out of the box. Leverage that if you’re already using jQuery in your project. Otherwise, rolling it with vanilla JavaScript is the way to go.
Let me explain the JavaScript a little bit. As the comment says, if our .preloader
element doesn’t have the opacity
property set, then it will be equal to empty (""
) and we can set it to a value of 1 manually to make sure it displays when the document loads.
Once we know the opacity is set, then we set manipulate it. The whole function is wrapped in setInterval
and we check if the opacity
property every 100 milliseconds to see if it is greater than zero. As long as it is above zero, we decrease its value in 0.1 increment, which creates a smooth effect that fades the element out over time.
Once we hit zero opacity, we clearInterval
to stop the script from running infinitely. Feel free to play around with timing and decreasing points to fit your needs.
The last thing that left to do is to call the function. We’ll call it when the window loads:
window.addEventListener('load', fadeEffect);
We are intentionally using the load
event instead of DOMContentLoaded
because the DOMContentLoaded
event is fired when the document has been completely loaded and parsed. That means it doesn’t *wait for stylesheets, images, and subframes to finish loading* *before it executes*. The load
event can be used to detect a fully-loaded page, and that is exactly what we are looking for. Otherwise, the function would start before our CSS and SVG are ready.
Drop some content into the HTML and try things out. Here’s the demo again:
See the Pen
Preloader with JavaScript FadeOut by Maks Akymenko (@maximakymenko)
on CodePen.
Congratulations! You now know how to build a pretty nice loading effect using nothing but an image and a pinch of CSS and JavaScript. Enjoy!
There’s one really important thing missing here: the
.preloader
element needs an attribute ofpointer-events: none;
otherwise it will be impossible to interact with the elements on the page, because the invisible loader element will be covering everything (you can actually see this in the demo). Personally, I prefer to use a second class and animate the change with CSS to make it easier to manage the transitions. So it would be something like:And then your JavaScript would just add the
.loaded
class to the.preloader
element.This is a really crucial step! Thanks man
With jQuerys .fadeOut you won’t have to worry about pointer-event, because it hides the element after the animation with display: none
Hi, great idea, I was looking for something like this. I had a couple of questions
– couldn’t you use css keyframes and an infinitely repeating animation instead of Javascript?
– would it make sense to hide spinner on document load? Probably take the. preloader class off the element too, or even destroy the whole thing for simplicity
– should the css and js be inline in the html ( style, script) so that the spinner can be rendered as soon as possible?
Thanks
John
I’d simply apply display: none to “.preloader.loaded”. It’s much more straightforward, has better browser support and is more semantic.
Thanks a lot for sharing…….
@John:
– Yes, you could. The loader in this demo isn’t actually using JavaScript for the main animation, it’s using SVG animation. JavaScript is being used for the fade-out after the page loads. Personally, I like using SVG animations (or even CSS animations stored inside the SVG file) because it keeps everything self-contained as a single image file.
– Here we’re just hiding the element on document load, but normally I would destroy it, as well. As mentioned by Rudi, you could use jQuery’s fadeOut() method to both fade AND remove the loader. But if you aren’t using jQuery, you could also do it manually via setTimeout() that waits until the fade-out is completed and then removes the element.
– You could separate the CSS for the spinner into its own file that is placed first so that it gets loaded before anything else, though depending on what sort of content you are waiting to load it might not matter. I usually don’t and haven’t had any issues. But the JavaScript doesn’t execute until the page is finished loading anyway, so it doesn’t really matter where that goes.
Huh, I just did one of these! https://codepen.io/jeffedsell/pen/GRKLMEB
Nice
Am I the only one that hates page loaders? Reminds me of flash websites.
What’s next? 10mb one page websites with progress bars?
Its mostly used for websites that use things like threejs or other recource intensive processes that need a little bit to load. But the results are always stunning!
you could alternatively set preloader.style.display=”none” in the else condition (opacity=0)
If this was stack overflow I’d be voting for this answer.
What’s the reason for manually stepping down the preloader in increments of
0.01
in JS, instead of using a CSStransition
to do it a bit more smoothly? You could just do a transition between1
and0
?What happens when javascript fails? Won’t the overlay remain visible, making it seem like the page is taking forever to load. Isn’t there an argument that, if your page load/render is slow enough to warrant a “loading” indicator, you should instead spend your time on speeding up the load/render?
I’ve been confidently calling myself a web developer now for a few years and page loaders has always been a sort of Pandora’s box for me. I had my suspicions they weren’t very complicated to make from scratch; but, admittedly, I was simply too lazy to ever do any digging.
This write-up took me all of five actual minutes to read, and it’s totally demystified pager loaders for me.
Excellent work!
After page load page elements are not clickable due to z-index. How to handle that ?
Good catch! You can add
pointer-events: none;
to the.preloader
and that should take care of it.