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.