Skip to content

Send a notebook to someone who doesn't run Jupyter

The notebook is done and someone needs to see it — a PM, a client, or a colleague who has never opened Jupyter. Handing over the .ipynb gives them a setup task. Export the HTML and ship it instead: they get your results, not an environment to install.

Drop your Jupyter report here — get a live link

free, no account, live for 30 days.

Shipping…

The artifact dead-end

Every standard route forces your reader to battle Jupyter's tooling before they see the data: GitHub rendering requires repo access, nbviewer demands a public host first, and asking people to 'just install it and run the cells' is a favour nobody owes you.

Flattening it down is just as grim — screenshots butcher wide tables, and PDF exports mangle everything else. An nbconvert HTML export is already the finished report, outputs baked right in. It just needs a URL.

Two lines to a live link

Build your output like you already do, then hand the folder to the action:

Turn the notebook into HTML

jupyter nbconvert --to html --execute report.ipynb

.github/workflows/ci.yml

- uses: bitgate/ship-page-action@v1
  if: always()
  with:
    path: report.html

Or with plain curl — no account needed

curl -fsS -X POST https://ship.page/deploy \
  -H "Content-Type: text/html" \
  --data-binary @report.html | jq -r .url

path: report.html deploys exactly what the tool wrote — no detection to configure. The link goes in the job summary — add comment: true and it lands on the pull request too.

What you get

  • The export is a true snapshot: code, outputs, tables, and charts frozen mid-run — your reader needs a browser, not a kernel
  • --execute runs every cell top-to-bottom on a fresh kernel at export, ensuring the HTML can't ship stale outputs left behind three sessions ago
  • Images embed as base64 in one self-contained HTML file — a single upload, nothing else to ship
  • Access control lives in the link: an unguessable slug, noindex headers, zero public index
  • Anonymous drops stay live 30 days; an sp_ key and a name lock in one stable URL for a recurring report

Downloadable example

A real export, end to end

A synthetic notebook — just a pandas table and a matplotlib chart — executed and exported with the exact command above. Open it in a new tab, or download it and re-upload it yourself.

Questions

Do the interactive parts still work?

Anything that runs on pure front-end JavaScript keeps working; anything expecting a round-trip to a Python kernel does not — there is no kernel behind a static file. Widgets freeze in their last rendered state. You're sharing a snapshot of results, not a live execution environment.

How do I make sure the outputs are fresh?

Export with --execute: nbconvert executes the notebook top to bottom against a fresh kernel before emitting the HTML. Without it, you export whatever stale state you last saved to disk — including that stack trace you forgot was sitting in cell nine.

What does the recipient need installed?

A browser. The export is a single self-contained HTML file — CSS inlined, images embedded — served byte-for-byte from its own dedicated subdomain. No Jupyter, no Python, no account.

Can I keep one URL for a report that regenerates?

Yes: deploy with an sp_ API key and a name, and every subsequent run overwrites that exact URL in place. In CI, passing the action a name: input handles updates on schedule — the link your team bookmarked never goes stale.

How is this different from nbviewer?

nbviewer fetches and re-renders your .ipynb from a public URL, on its own schedule and with its own styling opinions. This serves your exported HTML directly: the exact styling you shipped, private by obscurity, gone when it expires.

Related recipes

Try it on your next run

Anonymous deploys need no account and no key — paste the step, get a link. When you want stable URLs that redeploy in place, an API key and a name are all it takes.

Need to share the report with someone outside GitHub? How to share a GitHub Actions artifact without a login.