Publish a Plotly Dashboard Behind Company SSO

Plotly's `write_html()` method exports any figure or dashboard as a fully interactive self-contained HTML file. Pan, zoom, hover, and filter are all preserved in the file.

Publish a Plotly Dashboard Behind Company SSO
TL;DR
`fig.write_html("dashboard.html")` then `dsp publish ./dashboard.html`. Your data team's Plotly dashboards live at permanent, company-gated URLs – no Dash server to run, no Heroku to maintain, no email attachments.

The problem

Plotly's write_html() method exports any figure or dashboard as a fully interactive self-contained HTML file. Pan, zoom, hover, and filter are all preserved in the file.

Then it has nowhere to go. Email strips the JS, Google Drive and Slack both download it as a file, and corporate mail filters block HTML attachments outright.

The alternative, a Dash server, requires running and maintaining a Python web server, managing dependencies, and handling auth separately. For a monthly KPI dashboard, that's significant overhead.

display.dev is the middle path: static file, permanent URL, company auth.


Two steps

import plotly.express as px
import pandas as pd
 
df = pd.read_csv("sales_data.csv")
fig = px.line(df, x="month", y="revenue", color="region",
              title="Revenue by Region — Q1 2026")
 
fig.write_html("q1-revenue.html", include_plotlyjs="cdn")
dsp publish ./q1-revenue.html --name "q1-revenue-dashboard"

Share the URL. Anyone with a company email clicks it, authenticates once, and sees the full interactive Plotly chart: zoom, pan, hover tooltips, legend filtering.


Multi-chart dashboards

from plotly.subplots import make_subplots
import plotly.graph_objects as go
 
fig = make_subplots(rows=2, cols=2, subplot_titles=["Revenue", "CAC", "Churn", "NPS"])
 
fig.update_layout(height=800, title_text="Q1 2026 Executive Dashboard")
fig.write_html("executive-dashboard.html", include_plotlyjs="cdn")
dsp publish ./executive-dashboard.html --name "executive-dashboard"

The dashboard URL stays the same each quarter. Update the data, re-run the script, fetch the current version with dsp get <shortId>, then re-publish with dsp publish ./executive-dashboard.html --id <shortId> --base-version <currentVersion> (using the shortId from the first publish) to replace the existing artifact in place.


Before you share

A 30-second check before you send the link:

  • Open it in an incognito window to confirm it loads clean for someone who isn't signed into your machine.
  • If you exported with include_plotlyjs="cdn", viewers need internet to load Plotly.js. For an offline or air-gapped audience, export with include_plotlyjs=True so the chart is self-contained.
  • Open it on a phone. Half your stakeholders will.
  • Confirm the data is the run you meant to publish, not a sample left over from testing.

Who viewed it, and feedback in place

Because every viewer signs in with their company account, the artifact's view record shows which identities opened the dashboard and when. For a monthly KPI dashboard, that answers "did leadership actually look at this" without an anonymous counter. (How to know who opened your link.)

Reviewers also leave inline comments pinned to a specific chart, so "why did churn spike in March?" lands on the March data point instead of a separate Slack thread. You update the data, fetch the current version, republish with the artifact ID and base version, and resolve the comment. That loop is the part a static export, a PDF, or a screenshot can't give you. For the same workflow on a written report rather than a chart, see sharing an AI-generated report with stakeholders.


FAQ

Does this work for Chart.js, D3, or other dashboards, not just Plotly?+

Yes. Any self-contained interactive HTML dashboard publishes the same way. Plotly's write_html() is one path to a single HTML file; a Chart.js or D3 dashboard bundled into one HTML file (or a directory) publishes with the same dsp publish command and renders with its interactivity intact.

What about Dash applications?+

Dash requires a running Python server, not a static file. display.dev publishes static HTML. For Dash apps that need to stay interactive with live data, you'd need server-side hosting. For Dash apps that can export their current state as a static snapshot, use fig.write_html() on the underlying figures.

Does `include_plotlyjs="cdn"` mean viewers need internet access?+

Yes, "cdn" loads Plotly.js from a CDN. For fully self-contained files (no CDN dependency): use include_plotlyjs=True. The file will be larger (~3MB) but works without internet access.

Can I update the dashboard without changing the URL?+

Yes. Re-publish with --id <shortId> and the current baseline version: dsp publish ./executive-dashboard.html --id 8f3kx9 --base-version <currentVersion>. The URL stays the same; the content updates. Publishing without --id creates a new artifact instead.

Publish your first artifact in 15 seconds.

Free tier. No credit card. One-time password auth for viewers on free, Google + Microsoft SSO on Pro (€49/month flat).

Get started free →See pricing