4.0 is here: Offline Only, Express First, Displayable Anywhere

Published in
9 min readJul 22, 2019

-- is the library that powers graphs and maps for Dash. Version 4.0 of (also known as the plotly module) is now available for download from PyPI. It includes some exciting new features and changes, including a switch to “offline” mode by default, the inclusion of Plotly Express as the recommended entry point into the library, and a new rendering framework compatible with not only Jupyter notebooks but other notebook systems such as Colab, Azure and Kaggle notebooks, as well as popular IDEs such as PyCharm, VSCode, Spyder and others.

To upgrade to the latest version, you can run pip install plotly==4.1.0 or conda install -c plotly plotly==4.1.0. More details can be found in our Getting Started and Migrating to Version 4 guides. Read on for a detailed explanation of what has changed and why, or check out the changelog for an itemized list.

Offline Only

Prior versions of plotly contained functionality for creating figures in both “online” and “offline” modes. In “online” mode, figures were uploaded to an instance of Plotly’s Chart Studio service and then displayed, whereas in “offline” mode figures were rendered locally. This duality has been a common source of confusion for several years, and so in version 4 we are making some important changes to help clear this up.

Starting with this version, the only supported mode of operation in the plotly package is “offline” mode, which requires no internet connection, no account, no authentication tokens, and no payment of any kind. Support for “online” mode has been moved into a separately-installed package called chart-studio.

Version 4 of plotly includes no code or functionality for uploading figures or data to any cloud service.

Express First

Earlier this year we released a standalone library called Plotly Express aimed at making it significantly easier and faster to create plotly figures from tidy data—as easy as a single line of Python. Plotly Express was extremely well-received by the community and starting with version 4, plotly now includes Plotly Express built-in (accessible as and our documentation has been reworked to include examples using Plotly Express at the top of every applicable page, and to provide examples of how to customize figures generated with Plotly Express. is to plotly what seaborn is to matplotlib

Please note that if you’ve already started using the standalone plotly_express package, then you can upgrade to the latest version 0.4.0 right now, which uses under the hood.

Creating figures with Plotly Express is as easy as import as px and then passing your tidy Pandas data frame to the appropriate figure-generation function such as px.scatter() or Plotly Express supports a wide variety of charts, including otherwise verbose-to-create animations, facetted plots and multidimensional plots like Scatterplot Matrices (SPLOMs), Parallel Coordinates and Parallel Categories plots.

This is just a small sample of the charts that can be made in a single line of Python with Plotly Express

Displayable Anywhere

In addition to “offline” mode having become the default and only behavior, the plotly.offline package has been reimplemented on top of a new extensible renderers framework which enables Plotly figures to be displayed not only in Jupyter notebooks, but just about anywhere. Out of the box, Plotly figures now have the ability to display themselves in the following contexts:

Classic Jupyter Notebook

Additional renderers can be added to the framework as new target contexts arise, and renderers can be combined to produce sophisticated context-dependent static-vs-interactive behaviors.

As you might notice in our documentation, although the previously-recommended style of plotly.offline.iplot(figure) still works, the version 4 recommended way to imperatively display figures is to call, or you can simply let them display themselves by having a statement that returns a figure as the last statement in your notebook cell. Similarly, for exporting figure as HTML files we recommend moving away from plotly.offline.plot(figure) towards the new figure.write_html() method.

A New Default Theme

We introduced theming support in version 3.4. Since that release, we have iterated on the design of the built-in "plotly" theme, and we made this theme the default in the initial release of Plotly Express. In version 4, we are making this theme the default for all figures created with plotly. See the new theming and templates documentation page for information on configuring the default theme (including how to restore the old defaults!) and creating your own themes.

The new default theme

New Figure Update Methods

One of our goals for the integration of Plotly Express was to make it easy for users to start with Plotly Express for data exploration, and then tweak and refine the resulting figures with all of the customization support built into plotly.

To this end, version 4 introduces a suite of new figure methods for updating figures after they have been constructed. These functions all return a reference to the calling figure, and are designed to support being chained together (which makes them handy to use with Dash in the middle of a layout definition). We’ve also introduced “magic underscore notation” to make it easier and less verbose to update nested figure properties — this was a suggestion that came out of our Julia community! See the new Creating and Updating Figures page for full details, or check out this simple example of three equivalent ways to change legend orientation:

# the old way: one .update() method, deeply nested arguments
# the new way: a dedicated update_layout method
# the new new way: with magic underscores

Here’s an example of how to use the new update methods in a chain, with some magic underscores, to customize a figure created with Plotly Express by modifying the traces, axes and layout parameters:

import as px
df =
(, x="size", y="tip", facet_row="time", facet_col="day",
color="sex", title="Tips by Payer Sex, Party Size, Day and Meal")
.update_layout(title_font_size=24) )
Customizing Plotly Express charts with new update methods

More Flexible Subplots

The make_subplots function is used to create figures with custom subplot layouts, and it has received a lot of attention for version 4. In addition to fixing many bugs, make_subplots now supports all subplot and trace types: 2D, 3D, polar, ternary, maps, pie charts, sunbursts, Sankey diagrams etc. See the subplots documentation page for more information. The make_subplots function now also supports easy creation of dual-Y-axis plots.

The new figure update methods described above all support row, col, and secondary_y keyword arguments to be able to target in which subplot or on which Y-axis a trace should be added, or which exact axis should be modified. In keeping with the theme of this release, the Plotly Express internals were ported to use the new make_subplots functionality, so figures created with Plotly Express can be edited using these keyword arguments.

Here is an example that creates and populates a 2 x 2 subplot grid containing 4 different subplot types:

from plotly.subplots import make_subplotsfig = make_subplots(rows=2, cols=2,
specs=[[{"type": "xy"}, {"type": "polar"}],
[{"type": "domain"}, {"type": "scene"}]])
fig.add_bar(row=1, col=1, y=[2, 3, 1], )
fig.add_pie(row=2, col=1, values=[2, 3, 1])
fig.add_barpolar(row=1, col=2, theta=[0, 45, 90], r=[2, 3, 1])
fig.add_scatter3d(row=2, col=2, x=[2, 3], y=[0, 0], z=[0.5, 1])
fig.update_layout(height=700, showlegend=False)
Mixed-type subplots made with make_subplots()

And here’s an example of a dual-Y-axis chart with matching data and tick label colors:

from plotly.subplots import make_subplotsfig = make_subplots(specs=[[{"secondary_y": True}]])fig.add_scatter(secondary_y=False, x=[1, 2, 3], y=[40, 50, 60],
marker_color="MediumSlateBlue", name="Pressure")
fig.add_scatter(secondary_y=True, x=[2, 3, 4], y=[4, 5, 6],
marker_color="DarkOrange", name="Temperature")
tickfont_color="MediumSlateBlue", title="KPa")
tickfont_color="DarkOrange", title="° Celsius")
fig.update_layout(title_text="A dual-Y-axis chart")
A dual-Y-axis chart made with make_subplots()

JupyterLab 1.0 Support

The @jupyterlab/plotly-extension extension for rendering plotly figures in JupyterLab has been replaced by a new jupyterlab-plotly extension that will now be maintained and updated as a part of the project. Big thanks to the JupyterLab community for writing and maintain the original extension!

The new extension improves the load time of notebooks that contain lots of Plotly figures compared to the original extension. This is done by displaying saved figures as static images initially. These static images are then replaced with live Plotly.js figures when a user hovers over the plots.

The plotlywidget JupyterLab extension for rendering FigureWidget figures has been updated to support JupyterLab 1.0.

A smaller package: 4.7MB down from 27.4MB

In order to reduce the size of the core plotly distribution package, the bundled geographic shape files used by the create_choropleth figure factory have been moved to a new optional plotly-geo distribution package. This change reduces the size of the plotly source package from 27.4 MB down to 4.7 MB.

Non-breaking: graph_objs to graph_objects

The graph_objs submodule is now also available as the easier-to-pronounce graph_objects module. The graph_objs submodule is still supported for backward compatibility, but going forward we recommend using the following import statement to access the graph object classes:

import plotly.graph_objects as go

Still and Always 100% Dash-Compatible

Every plotly figure is still 100% Dash-compatible, and we hope that the new functionality will make it even easier to bring rich, compelling analytical applications into production. Here’s an example of a simple Dash app that uses some of plotly's new features such as Plotly Express, chained update methods and magic underscores:

import dash
import dash_core_components as dcc
import as px
def dynamic_layout():
df = # could be a database query instead!
return dcc.Graph(, x="day", y="tip")
app = dash.Dash(__name__)
app.layout = dynamic_layout

Ready to get started?

To upgrade to the latest version, you can run pip install plotly==4.1.0 or conda install -c plotly plotly==4.1.0. More details can be found in our Getting Started and Migrating to Version 4 guides or check out the changelog for an itemized list.


Like all releases, this was a team effort that built on the strong foundations laid by the Plotly.js team. Special thanks to lead maintainer Jon Mease for his steady hand at the helm of the ship, to Nicolas Kruchten for Plotly Express, to Emmanuelle Gouillart for her work on documentation, and to Chris Parmer & Fernando Pérez for advice and support. Finally, we’d like to thank our user community for all of the pull requests, bug reports and suggestions!




The low-code framework for rapidly building interactive, scalable data apps in Python.