Workbench: editing notebooks in the browser¶
A notebook page normally has one view: the rendered page. The workbench adds up to two more — Run and Edit — that open the same notebook in marimo's own editor, inside the page, with no server behind it. A reader's edits are kept in their browser as a local copy of the published notebook, with a version history, and survive reloads and republishes.
The Workbench demo page of this book offers all three views; try Edit in its header.
Enable it¶
toc:
- file: content/intro_to_pandas.py
views: [read, run, edit] # Read | Run | Edit in the header
open_in: read # optional; defaults to the first view
Any subset works, and defaults.views sets a book-wide default:
views |
What the reader gets |
|---|---|
[read] (default) |
The rendered page. Nothing from the workbench is shipped. |
[read, run] |
A Run button: the notebook in marimo's present view — every cell live and reactive, code hidden. Nothing is saved (a reader who already has a copy runs that copy). |
[read, edit] |
An Edit button: the full editor. Edits persist in the reader's browser; History and a copy-status chip appear. |
[edit] |
The page is the editor; no view control is rendered. |
open_in chooses the view a page opens in and must be one of that page's
views (marimo-book check enforces it). The current view rides in the
URL as ?view=edit, so a reload lands where the reader left off while a
link from the sidebar still opens the page view.
run and edit need the notebook to boot in Pyodide, like mode: wasm
pages: pure-Python dependencies install from the same PEP 723 block the
build stages (see Dependencies), and check warns when
a workbench notebook imports something with no Pyodide wheel.
What the reader sees¶
The header gains, left of the launch buttons: a status chip (published,
or ● your copy · 2 min ago), the Read | Run | Edit control, and
History. Switching to Run or Edit replaces the page body with the
editor, which boots marimo's Pyodide kernel — a few seconds on a laptop
the first time, faster once the assets are cached. Read is instant and
always shows the published page.
Edits are saved automatically about a second after each change, into the browser's IndexedDB (per browser and per profile; private windows and "clear site data" erase it). The first edit creates the reader's copy; from then on Edit opens that copy, and the header's download button serves it instead of the published file.
History opens a drawer with every version of the copy: the first
opening, automatic checkpoints (every checkpoint_minutes of editing, the
last max_checkpoints kept), versions the reader names with Save a
version, and the snapshots taken around updates and restores. Selecting
one shows a diff against the current copy; Restore brings it back (after
snapshotting the current state, so a restore is itself undoable). The
drawer's footer has Reset to published and Delete my copy, both
two-step.
When the book is republished¶
Every build stamps the page with a hash of the published notebook. When a reader whose copy started from an older version opens Edit, a banner says This notebook was updated. Your work is saved. with three choices:
- Update and keep my work replaces the copy with the new version after snapshotting the old one — with an Undo on the confirmation, and the previous copy in History as Before update.
- See what changed shows the diff between the reader's copy and the new version.
- Not now keeps the copy; the status chip keeps saying update available until they decide.
The reader's work is never overwritten silently. (Merging the reader's edits into the new version cell by cell is a planned refinement; today the update replaces the notebook and keeps the old copy one click away.)
What the build ships¶
Only when some page lists run or edit:
_workbench/assets/— a copy of marimo's frontend bundle, ~27 MB per marimo version. It has to be served from the site itself: the kernel runs in web workers, and browsers refuse worker scripts from another origin, so a CDN copy would show the editor but never start Python. The copy is skipped when the staged one already matches the installed marimo._workbench/index.html— the mount page the iframe loads: marimo's own page with a small script that installs the IndexedDB file store through marimo's publicfileStoresmount option._workbench/nb/<toc path>.py— the published notebook, with its PEP 723 block; its hash is what a reader's copy is compared against.javascripts/workbench.jsandstylesheets/workbench.css— the page shell (header controls, banner, history drawer), loaded site-wide and inert on pages without a workbench.
The editor lives in an iframe on purpose: marimo's stylesheet restyles
html, body and headings, and the Material page restyles marimo's, so
they need separate documents. Being same-origin, the frame shares the
site's storage and needs no sandbox attributes.
Assignments¶
A page can also carry a graded assignment — a separate notebook, opened in
a drawer at the bottom of the page with its own local copy, history and
grader sign-in — via assignment: on the TOC entry. There is a live
Assignment demo, and
Assignments and grading covers the
publishing flow.
Diagnostics¶
Append ?wblog=1 to a page URL before opening Edit and the Pyodide
workers' console output (package installs, kernel errors) is relayed to
the page console as [worker:…] lines — useful when a notebook boots but
a dependency does not install.
Limits¶
- One workbench per page; the notebook is the page's own
file:. - On a
mode: wasmpage the islands runtime still boots underneath a Run or Edit view, so two Pyodide kernels run at once. Preferviews: [read, edit]onmode: staticpages where the read view doesn't need to be reactive. - Phones get the Read view only; the control hides Run and Edit below ~720 px.
- Zensical's
```pyodidefences are a different, lighter tool for editing snippets in the reading flow; they have no notebook file, no reactivity and no persistence, so they don't replace the workbench.