Change "Loading..." to a spinner

I would like to replace the “Loading…” text that shows until a Dash app has loaded with a more fancy-looking spinner. Before diving into the CSS myself, I wanted to ask if anyone has done it before?

And if yes - how? :slight_smile:

1 Like

I’ve done this in dash-bootstrap-components docs, see relevant CSS here. The basic idea is to hide the existing spinner by targeting the _dash-loading CSS class, then attach your custom spinner with ::after.

In the above example the spinner-border argument to animation is a webkit animation defined by Bootstrap so that exact code won’t work out the box if you’re not using Bootstrap, but the same principle can be applied to other CSS spinners. For example I took one of the spinners from here and fiddled around a bit to get the following example.

import time

import dash
import dash_html_components as html


def slow_serve_layout():
    time.sleep(5)
    return html.Div("Hello, world!")


app = dash.Dash()

app.layout = slow_serve_layout


if __name__ == "__main__":
    app.run_server(debug=True)
._dash-loading {
  margin: auto;
  color: transparent;
  width: 2rem;
  height: 2rem;
  text-align: center;
}

._dash-loading::after {
  font-size: 60px;
  content: "";
  display: inline-block;
  width: 1em;
  height: 1em;
  color: #000;
  border-radius: 50%;
  margin: 72px auto;
  vertical-align: text-bottom;
  -webkit-animation: load6 1.7s infinite ease, round 1.7s infinite ease;
  animation: load6 1.7s infinite ease, round 1.7s infinite ease;
  margin-top: 2rem;
}

@-webkit-keyframes load6 {
  0% {
    box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em,
      0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
  }
  5%,
  95% {
    box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em,
      0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
  }
  10%,
  59% {
    box-shadow: 0 -0.83em 0 -0.4em, -0.087em -0.825em 0 -0.42em,
      -0.173em -0.812em 0 -0.44em, -0.256em -0.789em 0 -0.46em,
      -0.297em -0.775em 0 -0.477em;
  }
  20% {
    box-shadow: 0 -0.83em 0 -0.4em, -0.338em -0.758em 0 -0.42em,
      -0.555em -0.617em 0 -0.44em, -0.671em -0.488em 0 -0.46em,
      -0.749em -0.34em 0 -0.477em;
  }
  38% {
    box-shadow: 0 -0.83em 0 -0.4em, -0.377em -0.74em 0 -0.42em,
      -0.645em -0.522em 0 -0.44em, -0.775em -0.297em 0 -0.46em,
      -0.82em -0.09em 0 -0.477em;
  }
  100% {
    box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em,
      0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
  }
}
@keyframes load6 {
  0% {
    box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em,
      0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
  }
  5%,
  95% {
    box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em,
      0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
  }
  10%,
  59% {
    box-shadow: 0 -0.83em 0 -0.4em, -0.087em -0.825em 0 -0.42em,
      -0.173em -0.812em 0 -0.44em, -0.256em -0.789em 0 -0.46em,
      -0.297em -0.775em 0 -0.477em;
  }
  20% {
    box-shadow: 0 -0.83em 0 -0.4em, -0.338em -0.758em 0 -0.42em,
      -0.555em -0.617em 0 -0.44em, -0.671em -0.488em 0 -0.46em,
      -0.749em -0.34em 0 -0.477em;
  }
  38% {
    box-shadow: 0 -0.83em 0 -0.4em, -0.377em -0.74em 0 -0.42em,
      -0.645em -0.522em 0 -0.44em, -0.775em -0.297em 0 -0.46em,
      -0.82em -0.09em 0 -0.477em;
  }
  100% {
    box-shadow: 0 -0.83em 0 -0.4em, 0 -0.83em 0 -0.42em, 0 -0.83em 0 -0.44em,
      0 -0.83em 0 -0.46em, 0 -0.83em 0 -0.477em;
  }
}
@-webkit-keyframes round {
  0% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  100% {
    -webkit-transform: rotate(360deg);
    transform: rotate(360deg);
  }
}
@keyframes round {
  0% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  100% {
    -webkit-transform: rotate(360deg);
    transform: rotate(360deg);
  }
}
1 Like

Thank you for an amazing answer, @tcbegley ! That was exactly what I was looking for :smiley:

1 Like

@tcbegley reviving an old thread since the answer was so good. :slight_smile:

What should I do if want to display a simple png logo instead?

You can use the same approach as above, passing the URL of the image to content. This worked for me.

._dash-loading {
  margin: auto;
  color: transparent;
  width: 100vw;
  height: 100vh;
  text-align: center;
  position: absolute;
  top: 0;
  left: 0;
}

._dash-loading::after {
  content: url("https://upload.wikimedia.org/wikipedia/commons/a/a5/Hourglass_icon.png");
  horizontal-align: -50%;
}

4 Likes

Thank you! That worked perfectly.