Showing loading icon while app loads

I have been trying to resolve this the last few days, but with no success. I want to show a spinner while a bokeh app loads. I can either get a spinner that doesn’t disappear when the charts have loaded, or get no spinner at all. But I can’t get one that shows only while the charts are in the process of loading.

The app has a “templates” folder with index.html, styles.css and main.js. My problem is (I think) I don’t know what code to be using in the js file to show the app has finished loading.


            body {}
        <meta charset="utf-8">
        {{ bokeh_css }}
        {{ bokeh_js }}
             {% include 'styles.css' %}
        <div class="content">
            {% include 'main.js' %}
        <div id="loading"></div>
        {{ plot_div|indent(8) }}
        {{ plot_script|indent(8) }}


@import url(;

body { margin-top: 100px; background-color: #137b85; color: #fff; text-align:center; }

h1 {
  font: 2em 'Roboto', sans-serif;
  margin-bottom: 40px;

#loading {
  display: inline-block;
  width: 50px;
  height: 50px;
  border: 3px solid rgba(255,255,255,.3);
  border-radius: 50%;
  border-top-color: #fff;
  animation: spin 1s ease-in-out infinite;
  -webkit-animation: spin 1s ease-in-out infinite;

@keyframes spin {
  to { -webkit-transform: rotate(360deg); }
@-webkit-keyframes spin {
  to { -webkit-transform: rotate(360deg); }


window.addEventListener("load", () => {
    document.querySelector('#loading').style.display = "none";

I originally posted on SO, but as I didn’t get a response, and had spent some more time working on this since, am posting here instead.

I’ve managed to get the loading spinner icon to appear and disappear, using

def callback(event):


curdoc().on_event(DocumentReady, callback)

But the the icon appears only just before the charts. There is still a long period of of white space before the icon comes up. I tried adding an file with this code, but this made no difference.

from bokeh.models.widgets import Div
spinner_text = """
<!-- -->
<div class="loader">
<style scoped>
.loader {
    border: 16px solid #f3f3f3; /* Light grey */
    border-top: 16px solid #3498db; /* Blue */
    border-radius: 50%;
    width: 120px;
    height: 120px;
    animation: spin 2s linear infinite;

@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
div_spinner = Div(text=spinner_text,width=120,height=120)

def on_server_loaded(server_context):