Plotly without Dash

Plotly without Dash

While Plotly’s Dash looks like a great project for getting data visualization dashboards up and running quickly, it may not always be the best option, especially for dashboards that over time evolve into complex apps.

So I’ve been looking into harnessing the power of Plotly’s excellent graphing tools, while running technologies that can grow with the app. This post will be somewhat of a note-to-self, to keep track of the viable options for utilizing Plotly on standard technology stacks.

Flask + Plotly Express

Plotly Express is a high level Python wrapper for Using nothing but Python, HTML and CSS, we can generate websites with graphs generated by Plotly.

First, install Flask, and install the dependencies we’ll need: pip3 install flask plotly. Next, create the folder /templates, and add a file graph.html with this content:

        <script src=""></script> 
        <h1>Plotly Demo</h1>
        {% block content %}
        {{plot | safe }}
        {% endblock %}

Next, make sure your app looks something like this:

from flask import Flask, render_template
import as px

app = Flask(__name__)

def express():  
    df =
    fig = px.scatter(df, x="sepal_width", y="sepal_length", color="species")
    plot_as_string = plotly.offline.plot(fig, include_plotlyjs=False, output_type='div')
    return render_template("graph.html", plot=plot_as_string)

Type FLASK_APP=app flask run to run the app, and visit http://localhost:5000 in your favorite web browser. Congratulations! You now have a simple, lightweight Python based server that renders beautiful Plotly graphs.

REST API + React + Plotly

Next, we’ll take a look at a building the user interface in React.


Our backend will consist of a REST API served by Flask. Setup:

mkdir backend
cd backend
python3 -m venv env
source env/bin/activate
pip install flask pandas flask-cors

Create the file backend/, and fill it with this content:

from flask import Flask
import pandas as pd
import json
from flask_cors import CORS, cross_origin

app = Flask(__name__)

cors = CORS(app)
app.config['CORS_HEADERS'] = 'Content-Type'

def our_first_graph():
    data = [
            "x": [1, 2, 3],
            "y": [2, 6, 0],
            "type": 'scatter',
            "mode": 'lines+markers',
            "marker": {"color": 'red'},
        {"type": 'bar', "x": [1, 2, 3], "y": [2, 5, 3]},
    data_as_json = json.dumps(data)
    return data_as_json

Now, start the server: FLASK_APP=api flask run.


The frontend will be a standard React application. Setup:

mkdir frontend; cd frontend
npm install react-plotly.js plotly.js axios
npx create-react-app gui

Run cd gui/src. Create the folder utils, and add API.js:

import axios from "axios";

export default axios.create({
  baseURL: "http://localhost:5000/",
  responseType: "json"

Create the file Graph.js:

import React, {useState, useEffect} from 'react';
import Plot from 'react-plotly.js';
import api from './utils/api';

export default function Graph() {
    const [dataFromApi, setDataFromApi] = useState([]);
    useEffect(() => {
        async function fetchData() {            
            let userData = await api.get('/')
    }, []);

    return (
        layout={ {width: 320, height: 240, title: 'A Fancy Plot'} }

Adjust App.js to look something like this:

import './App.css';
import Graph from './Graph';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <Graph />    

export default App;

Now, start the server like this: BROWSER=none npm start. Open http://localhost:3000 in a browser, and see the result.

Final thoughts

We’ve looked at two – of many – approaches to building apps that use Plotly’s excellent libraries to visualize data. The two approaches both has their pros and cons. Using Plotly Express, we bundle both backend and frontend into a single server, which makes deployment quite a lot simpler.

On the other hand, separating the backend and frontend like we did with REST API + React, we it’s easier to grow, add or even replace either the frontend or backend. The downside though is a more complex setup with more moving parts and somewhat more difficult deployment.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: