Share a pandas DataFrame as HTML
The table is right there in the notebook and someone wants it. The export step has one trap in it.
to_html() gives you a fragment
df.to_html("table.html")
That writes exactly this:
<table border="1" class="dataframe">
<thead>
…
</thead>
<tbody>
…
</tbody>
</table>
No <html>, no <head>, no CSS. Browsers are forgiving enough to
render it, which is why the result looks like an unstyled table rather
than an error — and why it looks nothing like what you saw in the
notebook.
Styler gives you a page
styled = (
df.style
.format({"revenue": "${:,.0f}", "margin": "{:.1%}"})
.background_gradient(subset=["margin"], cmap="RdYlGn")
.hide(axis="index")
)
styled.to_html("table.html", doctype_html=True)
Styler.to_html() emits the generated <style> block with the table,
and doctype_html=True wraps it in a real document. Everything you set
in the notebook — number formats, conditional colouring, hidden index —
comes through. This is the one to send.
Useful arguments either way:
escape=False— render HTML you built in a cell (links, badges) instead of escaping it.max_rows=/max_cols=— truncate with an ellipsis row rather than emitting a table nobody can scroll.index=Falseonto_html(),.hide(axis="index")on the Styler.
Publish it instead of sending the file
Drag table.html into Comma and you get a
URL:
- Renders faithfully, including your Styler CSS.
- Opens on a phone, which an
.htmlattachment does not — see someone sent me an HTML file. - Access per report: private, team, anyone signed in, or link holders with view, comment or edit — sharing & access control.
If you built a fuller page — a couple of tables, a chart, some prose — publish that page instead. Plotly figures, folium maps and profiling reports all publish the same way: Plotly HTML, folium maps, ydata-profiling.
From a script or a schedule
import requests
html = df.style.format("{:,.2f}").to_html(doctype_html=True)
requests.patch(
f"https://commareports.com/api/v1/reports/{report_id}",
headers={"Authorization": f"Bearer {token}"},
json={"title": "Daily revenue by region", "html": html},
timeout=30,
).raise_for_status()
Same id, same URL, one revision per run — so "revenue by region" is a living link rather than a new attachment every morning. Put it on a timer with scheduled HTML reports, or publish from the job that already builds it — publishing from CI.
Then: comment on the cell
Anchored threads mean a correction lands on the number it's about and survives the next run — commenting on HTML and stop screenshotting reports.
Limits
Entry HTML 5 MB — which a wide table hits at roughly tens of thousands
of rows. Truncate with max_rows, or publish the aggregate. Assets:
25 MB per file, 250 MB and 500 files per report.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.