How to Make a Preloader

If your website contains lots of images, users might see them load onto the page one by one. This is perfectly fine most of the time, but in some cases, you may want all your assets to load before your users see any real content - perhaps to give your users a more cohesive visual experience when they enter your site. To achieve that, we’re going to build this preloader:

Your Beautiful Website!

For a real-life example, visit my portfolio website and check out its GitHub repo. In case you don’t have time to meticulously sift through unfamiliar code, here are the relevant HTML, SCSS, and JavaScript.


Let’s start with the HTML. Put this block at the top level of your site - you could place it as a direct child of the <body> tag, or as a child of whatever element contains your site’s main content.

<div class="preloader">
  <div class="preloader__wall preloader__wall--left"></div>
  <div class="preloader__wall preloader__wall--right"></div>
  <div class="preloader__spinner"></div>


The Walls

Now for the fun stuff! This will place the preloader walls side-by-side and make each one cover its half of the window.

.preloader__wall {
  background: #2eaee0;
  outline: solid 1px #2eaee0; // prevents 1px gap between the walls
  position: fixed;
  width: 50%;
  height: 100%;
  transition: transform 0.7s 0.2s;
  z-index: 100;

  &--left {
    left: -50%;
    transform: translateX(100%);

  &--right {
    right: -50%;
    transform: translateX(-100%);

Because each wall has width: 50% and left: -50% or right: -50%, they would actually be positioned outside of the window if it weren’t for their transform properties. Later, we’re going to set transform: none to move each wall out of the window when the rest of your website is done loading.

It might be helpful to read up on the transition and transform properties.

The Spinner

I adapted the spinner from this example. It’s basically a rotating object with a multicolored border. Try temporarily commenting out the border-radius to see what I mean.

.preloader__spinner {
  border: 6px solid white;
  border-top: 6px solid #f57d4e;
  border-radius: 50%;
  position: fixed;
  width: 60px;
  height: 60px;
  left: 0;
  right: 0;
  top: 50%;
  margin: auto;
  z-index: 100;
  animation: spin 1.2s linear infinite;

@keyframes spin {
  0% {
    transform: translateY(-50%) rotate(0deg);
  100% {
    transform: translateY(-50%) rotate(360deg);

It might be helpful to read up on the animation property.

The “Finished” State

We’re going to add the preloader--finished class onto the preloader element with a bit of JavaScript in the next section. This will move the preloader walls out of the window and hide the spinner.

.preloader--finished {
  .preloader__wall {
    transform: none;
    outline: none;

  .preloader__spinner {
    display: none;


Now all we have to do is put the preloader into its finished state when the page is done loading. The window’s onload event fires when all the content on the page (including the DOM, images, scripts, etc.) has loaded, so we’re going to set a handler function to this event that finds the preloader element and adds the preloader--finished class to it.

window.onload = () => {
  const preloader = document.getElementsByClassName('preloader')[0];

You could alternatively use addEventListener like below. This is useful when you want to add multiple listeners to the event.

window.addEventListener('load', () => {
  const preloader = document.getElementsByClassName('preloader')[0];

That’s it! Feel free to email or message me if you have any questions or feedback.

A web/game programming blog by Robyn Choi, a professional JavaScripter and amateur pixel artist in Vancouver, BC. To see some of my work, check out my portfolio website.

All Posts