Plotly Dash or React.js + Plotly.js? A side by side comparison

Dash value proposal is to hide the complexity of writing a React Web App while presenting some data in a dashboard. Let’s do a side by side comparison with a machine learning use case

Antoine Hue
Towards Data Science

--

We have seen in “Which library should I use for my Python dashboard?” that creating a data visualization in a Web dashboard is quickly getting complex and requires a framework that is doing more than stitching some plots. Plotly Dash is one such framework and is very reachable to most data scientists as the application is pure Python code.

Dash and React.js leading you to the top. Photo by Stéphane Fellay on Unsplash

At the same time, Dash is generating a Web application built on React.js, a leading Javascript framework for Web applications. Sooner or later you will feel limited by Dash. Either by the constraints or the principles of Dash, or by the lack of control on the generated app, or by the integration of the dashboard with a larger application. For example, your application may require authentication of the user, it may fetch some client data and eventually lead to the Dashboard.

What if I want to switch from Dash to a classical React.js application after my prototyping phase? Is this costly?

What if I want to skip completely the Dash phase and directly create a React.js app? Is the learning curve too steep?

In the following is shown what it means to write a Dash application and the React.js alternative, based on a use case that fits with Dash: upon user selection, the inference is performed on an Audio file using the YAMNet classifier. The inference is returning the scores as well as the waveform, these series are plotted on the dashboard.

Dashboard showing audio waveform and inference scores © the author

The code of the article is available in a Github repository tonio73/dash-to-react. This is a tutorial, more than a template to start your development. That’s why the steps to reproduce this code are explained here below.

Both versions of the dashboard are based on Plotly for the plots, Bootstrap 4 for the HTML and CSS design. The two are available as plugins to Dash and as Node.js packages for React.js.

Plotly Dash application

In folder dash_version/

In the case of Plotly Dash, all user code is in Python. Dash is not only generating the required Javascript code but also the Web API to create and update the content of the Web application in the browser. Dash is creating all this plumbing, and serving the code over HTTP using Flask (that shall be deployed over a strong web server in production: Gunicorn + Nginx recommended).

Layer architecture of the Dash application © the author

Creating a Dash application requires four steps:

  1. Install the required Python packages (ref)
  2. Create the application layout (ref) using base HTML Components from Dash Core Components (ref) or Dash HTML Components (DCC, ref), or with a more complete HTML framework like Bootstrap 4, wrapped into Dash Boostrap Components (DBC, ref)
  3. Create the Dash application with the above layout (ref)
  4. Add callbacks to handle user interaction (ref)

Steps 1 to 3 should be rather straightforward given the Dash documentation. Main design decisions are on how to present data in the visualization, how to set Plotly parameters. You will probably bump into the choice between Plotly’s APIs: Express or Graphical Object? Express is nice if the data is within a Pandas data frame. However, even in this case, it is quite limited to plotting homogeneous series. For more complex plots, Graphical Object is needed.

Step 4, callbacks, is trickier than the previous steps. It requires first to define the user interaction and second to find a way to express it in the Dash concepts. This is often quite complicated as soon as controls are chained (e.g. a select that is changing the options of another select that defines a plot). Here are a few recommendations:

  • If the user selection is required, do not initialize Plotly Figures while building the layout. The Figures are set within the callbacks.
  • If the data to plot requires computation or is quite heavy (more than 100 points), wrap the plots into a Loading component of Dash Core Components.

Wait! How is the data to display loaded? How is the inference performed?

These questions lead to another important concept of Dash: the server should not keep track of user-specific values, that is it should be stateless (ref). As there is no language boundary or package split between server and client (browser) software, it is very easy to create some state within the server. As a rule: to be stateless, callbacks shall not use variables that are not available from the callback function arguments.

However, it is sometimes recommended to keep some state. For example, to preload some big variables like the machine learning model that is used for inference. In our case, loading Tensorflow and the YAMNet model takes several seconds and should not be done at each inference call. It would be not only slow but also CPU and memory consuming.

React.js application with Flask API server

In folder react_version/

In this setup, the Python Web server is only acting as an API server exposing some REST end-points. The application is natively designed on React.js using the Plotly.js and React-plotly.js (ref1, ref2) packages. Bootstrap is also added as a dependency on the React.js application. The asynchronous calls to the API server are performed by Axios (ref).

React.js & Flask layer architecture © the author

Two servers are used during the development (can be gathered into a single server for production):

  • Flask is running a development server to develop and debug the API
  • The compiled React.js application, as well as other HTML dependencies (CSS, images…), are supplied by a server based on Node.js

Similarly, there are two lists of dependencies:

  • Python dependencies managed by Conda or Pip
  • Javascript dependencies managed by Node Package Manager (NPM)

The last major change is the need to compile and bundle the React.js application. This is handled by Yarn using Webpack and React-scripts.

The blog post How To Create a React + Flask Project provides a clean way to set up and run such a setup and to avoid security issues like cross-origin resource sharing (ref): the Node.js server is configured to act as a proxy for the API server: any unknown request is forwarded to the API server.

The next steps are the API design and the React.js application.

API server

In the tutorial’s application, the API server has two endpoints (file api/app.py):

  • /api/samples to get the list of available audio samples metadata (URL, title, filename)
  • /api/infer to perform inference on the user-selected sample

React.js application

In React with JSX (ref) components are instantiated hierarchically as custom HTML-like tags. Three components are used at the top level:

  • <audio> which is the standard HTML 5 audio player
  • <SampleList> that is fetching from the API server the list of samples and provides a dropdown to get the user's selection
  • <Scores> the is calling the inference on the API server and displaying sound waveform and inference scores.
Top-level application components in React.js version © the author

While being simple, this application is requiring a good understanding of React.js components’ concepts (ref):

  • The state and props within the components
  • The creation and update cycles of a component
  • Updating the parent’s state through callbacks

When the user is selecting a sample from the dropdown of <SampleList>:

  1. The selected value is passed to the parent (<App>)
  2. <App> is updating, <audio> with the URL of the static file on the server, and <Scores> with the file name of the sample to perform inference on
  3. <Scores> is updated and is calling asynchronously the server to perform inference
  4. <Scores> is rendered when inference is completed and the waveform and scores are received

Plotly.js

Plotly.js is the backbone of Plotly for both the Python and the React versions.

That should probably mean that Python statements are massaged to produce the React code… However, for some obscure reason, Plotly-react.js is not theming the plots, the template property of the plot needs to be defined. Using the Chrome inspector, this property is copied from the Dash version. At this time, it is observed that Plotly Python is quite lazy: the full template containing theming for all types of graphs is always transmitted to the client and for each graph. This seems like some overhead that could be decreased if there were a default template in the Javascript component.

Also, React build tools are complaining that the application bundle is too large and should be split (ref). Using Source-map-explorer (ref), I have found out that this is not possible as almost all of the bundle weight is the Plotly package.

Wrap-up

The Dash implementation is quite neat, 1 file, only 47 statements, a single language. At the same time, there is already a learning curve to get around the formalism of the Dash callbacks. It gets a lot more arduous when the application is more complex with more components, multi-pages. Integration into a more complex Flask application would be required if the application is handling authentication, or is editing some data in forms.

However, writing the React application is not that complex too:

  • 1 Python file with 24 statements
  • 6 Javascript files, all of them with few statements

The learning curve is steeper, even more, if Javascript or React have to be learned first. There are more tools, more concepts. But eventually, the framework (React.js) is more coherent, more powerful, and supported by a wide community.

Dash is a good fit for a simple application as the one shown, and for internal use only. As soon as the application gets a bit bigger, or the user interface needs more refinements, it is better to switch to a React.js application.

At the same time, Plotly having Python and Javascript versions is allowing for a smooth transition between the two implementations.

That’s your turn to try, code is available on Github:

https://github.com/tonio73/dash-to-react

--

--

Data and Computer Scientist, I want AI to be actionable and accessible to everybody