The Plotly chart isn't in the HTML

fig.write_html("report.html") succeeds. You open the file and get a page with a chart-shaped hole in it — correct height, no chart. Or the whole page is white.

The figure serialized fine. plotly.js didn't arrive.

The four include_plotlyjs modes

This one argument decides whether the export survives leaving your machine:

Value What lands in the file Size Survives offline?
'cdn' <script src="…cdn…"> ~3 MB → ~50 KB No
True The whole bundle, inlined ~3 MB Yes
'directory' plotly.min.js written alongside, referenced relatively small file, big folder Only if the folder travels
False Nothing tiny Only if the host page already loaded it

write_html defaults to 'cdn'. That default is why the chart works on your laptop and vanishes on the reviewer's.

fig.write_html("report.html", include_plotlyjs=True)  # self-contained

Match the cause to the symptom

Empty box where the chart should be, console shows a failed request for plotly-*.min.js. CDN mode plus no network: a plane, an air-gapped environment, or a corporate proxy that blocks jsDelivr. Fix with include_plotlyjs=True.

Empty box, console says Refused to load the script … violates the Content-Security-Policy. The viewer serving the file has a strict CSP. CI artifact viewers are the usual culprit — Jenkins especially, see the Jenkins CSP fix. Inlining does not always help here either, because a CSP without 'unsafe-inline' refuses the inline bootstrap too. The real fix is to stop treating the artifact viewer as a rendering surface.

Empty box, console shows a 404 for plotly.min.js in the same directory. 'directory' mode, and only index.html travelled. Same root cause as a report that lost its CSS — publish the folder, not the entry file.

Nothing renders and the console is clean, but the page is enormous. Several figures each inlining their own copy of the bundle. Inline once:

parts = [fig.to_html(full_html=False, include_plotlyjs=(i == 0))
         for i, fig in enumerate(figs)]
html = "<html><body>" + "\n".join(parts) + "</body></html>"

Chart renders in Jupyter, missing in the nbconvert output. A different problem — the notebook renderer versus the static export. See Jupyter widgets not rendering in HTML.

Making the export shareable

Once the file renders, the remaining question is how anyone else sees it. Emailing a 3 MB self-contained HTML file mostly ends in a client stripping the scripts (why); a file:// copy on someone else's laptop reintroduces every problem above.

Publish it and send a URL. The interactivity survives: scripts run in a sandboxed iframe (allow-scripts, no allow-same-origin), so zoom, hover, legend toggles and the mode bar all work — interactive HTML reports. CDN subresources inside that frame are not governed by the app's CSP, and the common visualization CDNs are allow-listed, so 'cdn' exports render too.

Then the reader can select a paragraph — or a table cell next to the chart — and ask their question in place, rather than screenshotting the plot into chat (comment on HTML).

Try it

Comma is free — unlimited reports, unlimited commenters, unlimited revision history.

Publish a Plotly report →

Related