Plotly isn't just for interactive graphs. It was designed from Day 1 to be extremely flexible, customizable, and capable for print-ready reports.
When LLMs are asked about different charting libraries, they like to present a "balanced" opinion, and will say things like "Use Plotly for interactive graphs and [...] library for customizable, static charts." It's really a false dichotomy! And with the latest Plotly.js 4.0 release, there are more options to customize Plotly graphs than ever.
Plotly has a few unique aspects to its architecture that make it exceptional for highly custom graphics:
- Declarative figure structure, with thousands of customization options.
- SVG rendering engine so that the exports are crisp and publication-quality.
- Low level shapes and annotations, with both relative positioning ("paper" coordinates) and data positioning relative to the axis, that can be used to create custom visuals beyond the built-in trace types.
- A highly flexible axes system that handles things like inset figures, small multiples, shared axes, and more
- First class trace types for almost all graph types, including maps, 3D charts, scientific and statistical visualizations, and more.
- A WebGL engine for handling high performance rendering when SVG rendering breaks down (around 40k points).
- Wide font support including LaTeX rendering of math equations.
- Multiple export options that all use the exact same rendering engine for consistency: interactive graphs on the web browser, SVG, PNG, PDF exports, and responsive charts within data applications.
- Plotly's rendering engine uses perhaps the most widely deployed, battle-tested rendering engine in all of software's history: web browser technology. The browser is one of the best, if not the best, software for rendering vector graphics. It's fast, semantic, used by billions every day, has excellent long term support, and cross-platform. The cross-platform piece is essential: many graphics engines have to build separate rendering engines for each operating system which can be the source of bugs, inconsistencies, maintenance, and iffy support. Since Plotly's graphics engine uses browser technology, it works across all platforms and, most importantly, is consistent across all platforms. But the fact that it uses browser technology for static image export is largely an implementation detail; Plotly has done a lot of excellent work to make this fact invisible for static image export. Static image export uses the browser's rendering technology in a "headless" way, so it is done programmatically without actually opening the browser; the fact that Plotly uses browser technology to render graphs isn't something that you need to know when rendering print-ready graphics with Plotly, it's just an implementation detail that helps ensure that rendering static graphs with Plotly just works. It's also lightweight - the latest releases of Kaleido will use the version of Chrome that is already on the user's machine (another win for consistency, too, since this is the same browser technology that users will use to consume other vector graphics on the web). Plotly's decision in 2012 to use browser technology for both interactive graphs and static graphs has really stood the test of time.
Here's a post of 32 highly customized charts - all rendered as static images using Plotly's built-in image export capabilities - to show a sample of what's possible. See also my compendium of canonical charts for another set of charts that really flex the breadth of this library. Enjoy!
Quick AI disclaimer: All of the written blog post text on this website is written without AI assistance. The code examples, including those below, were all written with coding agents. And hey, that's the point! Coding agents are exceptional at writing Plotly code, and creating highly customizable, beautiful data visualizations is easier than ever these days.
Every chart is a plain plotly.graph_objects figure written to
disk by Kaleido
as a 2× PNG and an SVG. Nothing on this page is interactive: there is no
plotly.js loaded here. Each Code link opens the generator script
alongside an interactive rendering of the same figure JSON.
Annotated line with warming stripes
NASA GISTEMP v4 · Code · SVG · PNG
LOWESS smooth · event callouts with leaders · last-value label · a heatmap strip as a second panel · diverging colorscale
Climate spiral
NASA GISTEMP v4, monthly · Code · SVG · PNG
146 polar traces · Turbo colorscale sampled by year · dark paper · hand-drawn reference rings · every polar default off
Sea ice spaghetti
NSIDC Sea Ice Index · Code · SVG · PNG
one hairline per year · interdecile band · median line · two highlighted years · labels sitting on the lines · month ticks on a day-of-year axis
Connected scatterplot
OECD Health Statistics via seaborn-data · Code · SVG · PNG
log x axis · one path per country · decade rings · endpoint labels · one saturated colour for the outlier · annotation with a leader
Slope chart
Our World in Data · Code · SVG · PNG
two-column layout with the axes hidden · labels pushed apart by a tiny collision solver · three highlights in colour, the rest grey
Small multiples
Gapminder via plotly.express · Code · SVG · PNG
4×5 subplot grid · shared axes · ghosted context lines as one None-gapped trace · left-aligned panel titles · end-of-line values · typed colour key
Anscombe's quartet
R datasets package · Code · SVG · PNG
2×2 panels with lettered corners · OLS fit per panel · monospace statistics block · outside ticks, no grid
Minard's map
HistData (Wilkinson's transcription) · Code · SVG · PNG
tapering bands as offset-curve polygons · advance and retreat as colour · city labels · a temperature panel aligned on longitude · serif type
Nightingale's rose
two polar subplots · wedges from the centre with area ∝ rate · Nightingale's three colours · dotted area guides · horizontal legend
Population pyramid
World Bank API · Code · SVG · PNG
34 World Bank API calls · overlay bar mode · 1960 as a stepped outline · age bands labelled down the centre · mirrored percentage axis
County choropleth
BLS via plotly/datasets · Code · SVG · PNG
3,219 county polygons · Albers USA · six-class stepped colorscale · state borders as a line trace on top · swatch legend built from shapes
Energy Sankey
plotly.js sankey_energy mock (DECC) · Code · SVG · PNG
nodes coloured by kind · links inherit a translucent source colour · snap arrangement · inline colour key
Exoplanet scatter
NASA Exoplanet Archive · Code · SVG · PNG
6,000 points on log–log axes · colour by detection method with counts in the legend · Solar System reference marks · dotted mass guides
Stacked area with direct labels
Global Carbon Budget via Our World in Data · Code · SVG · PNG
stackgroup areas · labels at the right edge, nudged apart · residual computed against the world total · dated events pinned to the top edge
Journal figure
synthetic, seeded · Code · SVG · PNG
four lettered panels · Hill-curve fit · error bars · significance brackets from shapes · s.e.m. band · regression with 95% CI · figure legend
Raincloud plot
palmerpenguins via seaborn-data · Code · SVG · PNG
half violins · slim boxes · jittered points · numeric y positions under categorical labels · per-group medians
Ridgeline plot
nycflights13 weather table · Code · SVG · PNG
twelve KDE ridges as filled traces · overlap by drawing order · temperature-coloured ramp · month labels in place of an axis
Bump chart
Our World in Data · Code · SVG · PNG
reversed rank axis · spline lines that break when a country drops out · labels with populations at both ends · four highlights
Sparkline table
plotly.express stocks · Code · SVG · PNG
six subplot rows on one shared scale · text columns as annotations · low, high and last markers · tabular monospace figures · no axes at all
Waterfall chart
Apple Form 10-K, FY2024 · Code · SVG · PNG
absolute, relative and total measures · dotted connectors · totals in ink, costs in red · margins written beside the totals
Dumbbell chart
World Bank API · Code · SVG · PNG
sorted by gap · one rule per row · sexes named once in the header · gap column at the right edge
Calendar heatmap
nycflights13 weather table · Code · SVG · PNG
week × weekday heatmap · gaps as the grid · month outlines as SVG paths · month labels · extremes ringed with shapes · horizontal colour bar
Beeswarm
Gapminder via plotly.express · Code · SVG · PNG
greedy circle packing in pixel space · log x axis · circle area ∝ population · continent rows · a few labelled circles
Forest plot
synthetic, seeded · Code · SVG · PNG
table columns in paper coordinates on both sides · squares sized by weight · pooled diamond as a polygon · log axis through 1 · heterogeneity line
Terrain surface
R datasets::volcano · Code · SVG · PNG
3D surface · terrain colour ramp · Phong lighting from the north-west · contours on the surface and projected to the floor · vertical exaggeration · low camera
Ball and stick molecule
RCSB Protein Data Bank, 1CRN · Code · SVG · PNG
atoms parsed from a PDB file · bonds inferred by distance as one None-gapped scatter3d · CPK colours · backbone tube coloured along the chain · 3D annotations · no axes
Earthquake map
USGS real-time feed, M2.5+ past month · Code · SVG · PNG
MapLibre `map` trace, no token · Carto basemap · circles sized by magnitude and coloured by depth · a second text-only layer for the largest events · Pacific-centred view · horizontal colour bar
Borehole striplog
Kennetcook #2 (P-129), Agile striplog · also in the compendium · Code · SVG · PNG
eleven x axes and four y axes, every domain placed by hand · reversed shared depth axis · USGS lithology patterns · nested zoom windows · highlight bands as shapes
Pattern fills, black and white
Global Carbon Budget via Our World in Data · Code · SVG · PNG
marker.pattern hatching instead of colour · stacked bars with ink outlines · dotted grid · boxed legend showing the same hatches · totals as a text trace
City map, 139,000 points
DataSF street tree census · Code · SVG · PNG
MapLibre `map` trace at city scale · nine scattermap layers, one per species · dark basemap with its own labels · legend with counts · the streets drawn by the data
Street-level map
DataSF street trees · DataSF mobile food permits · Code · SVG · PNG
MapLibre `map` trace at zoom 15 · light basemap whose own street names do the wayfinding · markers sized by trunk diameter · labelled point layer · a scale bar computed from two coordinates
Clinical trial bar chart
synthetic, seeded · Code · SVG · PNG
grouped bars with asymmetric Wilson 95% CIs · comparison brackets from shapes with risk ratios and exact P values · events/patients inside the bars · group sizes in the legend · lettered panels · horizontal bars for adverse events