Embedding Bokeh into a ReactJS app using BokehJS

This project highlights how to use BokehJS to (very) easily include Bokeh visualizations in an HTML webpage.

The project uses the following tech:

  • Bootstrapped with Create React App.
  • Bokeh is an excellent python vizualization library that is quite popular among data scientists. Bokeh Homepage
  • Flask is a python webserver microframework that allows you to quickly and easily define a webserver in python. Flask Homepage
  • BokehJS is a subset of the Bokeh project and makes it really easy to embed the visuals generated by Bokeh into a webpage. BokehJS Documentation

Background – why would I need this?

Bokeh is an extremely popular visualization library among python users, particularly among data analysts, and so on. To those who are already familiar with the python data ecosystem, think matplotlib on steroids.

Bokeh’s Github stats at the time of writing

Bokeh makes it relatively easy to create stunning and meaningful data visualizations which impart meaning to your data. Especially when used in conjunction with python notebooks, it makes your research and code more accessible to a wider audience

Bokeh allows for a large number of plots

However, not everyone knows how to use Python Notebooks, or is even aware of their existence. Or, for some reason, you’d like to keep your code private. Any of these reasons would be enough for you to consider serving a Bokeh visualization via the web – a medium which most users are extremely comfortable with.

So how would you serve a Bokeh graph via your typical frontend tools, like ReactJS? You could recreate the graph in some other javascript library – but your data science pals already went through the hassle of creating the visualization in the first place…

Enter BokehJS

BokehJS allows for easy transferring of visualizations from python to embedded JavaScript. The documentation lists a number of alternative ways to embed a graph in your webpage, in my opinion the easiest and what felt most natural to me, is to serve my graphs via a Flask server (I dont want to depend on a Bokeh server in production since DevOps are already familiar with frameworks like Flask and Django), and provide API endpoints for a ReactJS (or Angular or JQuery(?)…) frontend to get the visualizations.

Bokeh catered for this approach brilliantly. The Bokeh library includes the embed module which allows you to generate a json_item:

from bokeh.embed import json_item

The process is really simple:

  • Generate a Bokeh plot in python as you normally would. Follow any documentation online and get the visualization you’d like to display
  • Pass the resulting Bokeh plot object to the json_item function call, which takes two arguments: the actual plot object and a string unique identifier. This unique ID can match the HTML ID of a DIV in your frontend, but this is not absolutely necessary.
  • Dump the result as a JSON string using the standard python JSON library dumps

A simple example from beginning to end using Flask would be something similar to this:

@app.route('/plot1')
def plot1():
    # copy/pasted from Bokeh Getting Started Guide
    x = linspace(-6, 6, 100)
    y = cos(x)
    p = figure(width=500, height=500, toolbar_location="below",
                     title="Plot 1")
    p.circle(x, y, size=7, color="firebrick", alpha=0.5)

    # following above points: 
    #  + pass plot object 'p' into json_item
    #  + wrap the result in json.dumps and return to frontend
    return json.dumps(json_item(p, "myplot"))

That’s it on the back-end. The front-end is similarly simple:

  • Import the required JS libraries. You can use a CDN or npm.

Note regarding NPM usage: Unfortunately BokehJS uses both relative and absolute path imports in it’s codebase. This results in some “module not available” errors when using it in boilerplate generate by a tool such as create-react-app. You can see the full details here. In order to sidestep this issue I recommend using the CDN option and simply referring to the library via the global reference (e.g. window.Bokeh)

  • Issue a GET request to the above endpoint (I use Axios for this)
  • Parse the response as JSON (Axios does this automatically for you)
  • Pass the JSON response to window.Bokeh.embed, which takes two arguments: the JSON object and an optional identifier specifying which DIV ID you’d like to embed the resulting object in.

Using ReactJS, this can be boiled down to:

handlePlot1 = () => {
    Axios.get("http://localhost:5000/plot1")
          .then(resp => 
        window.Bokeh.embed.embed_item(resp.data, 'testPlot')
    )
  }

// in your render function
<Button 
     variant="contained" 
     style={{margin: 10}} 
     color="primary" 
     onClick={this.handlePlot1}
>
          Get Plot 1 
</Button>
        
<div id='testPlot' className="bk-root"></div>

Note the className set to ‘bk-root’ which allows BokehJS to properly style the resulting visualization.

Code example

A full boilerplate code example can be found here:

https://github.com/dvas0004/bokeh-react

The project puts the above into practice. It offers two plots, the first is a static but involved plot, while (best of all in my opinion) the second plot shows that BokehJS supports dynamic widgets such as sliders which allow a front-end user to explore the data (if you go down this route make sure to read the relevant documentation)

Screencast demo of project

Note: if you go down the CDN route like I did, make sure to include the correct <script> for JavaScript and <link> for CSS files in your index.html file