Skip to content

fix: preserve widget state in HTML export - #8933

Closed
dondetir wants to merge 1 commit into
marimo-team:mainfrom
dondetir:fix/html-export-preserve-widget-state
Closed

fix: preserve widget state in HTML export#8933
dondetir wants to merge 1 commit into
marimo-team:mainfrom
dondetir:fix/html-export-preserve-widget-state

Conversation

@dondetir

@dondetir dondetir commented Mar 31, 2026

Copy link
Copy Markdown

This pull request was authored with assistance from a coding agent.

Summary

Preserve widget states (dropdowns, sliders, plotly selections) in HTML exports instead of resetting them to defaults.

Problem

When exporting a notebook as HTML via "Download as HTML (except code)", widget states reset to their construction-time defaults. A dropdown set to "Critical" exports as "Normal"; a chart filtered to 2 countries exports showing all countries. The reader sees something different from what the author intended to share.

Root cause: session_view.ui_values (the dict tracking all widget interactions) is never included in the HTML export serialization.

Solution

Three coordinated changes that mirror the existing live-session reconnect pattern (handleKernelReady in handlers.ts:162-166):

  1. Python schema — Add ui_values: Optional[dict[str, Any]] to NotebookSessionV1
  2. Python serialization — Include ui_values=dict(view.ui_values) in serialize_session_view()
  3. Frontend — In mount.tsx, read session.ui_values and pre-populate UI_ELEMENT_REGISTRY before React renders widgets in static mode

Backward compatible

ui_values is Optional/nullable with #[serde(default)]-equivalent behavior. Old HTML exports without the field continue to work (widgets fall back to data-initial-value as before).

Re: PII concern

Per @mscolnick's note — widget values are already visible in the rendered cell HTML output, so this doesn't expose new data. Happy to add a changelog entry noting this behavior change.

Files changed (10 files, +35/-8 lines)

File Change
marimo/_schemas/session.py Added ui_values: Optional[dict[str, Any]]
marimo/_session/state/serialize.py Include ui_values in serialization
marimo/_schemas/generated/session.yaml Updated OpenAPI schema
packages/openapi/src/session.ts Updated TypeScript type
frontend/src/mount.tsx Hydrate UI_ELEMENT_REGISTRY in static mode
tests/.../snapshots/*.json (5 files) Updated snapshot expectations

Tests

  • Serialization tests: 32 passed, 1 xfailed
  • Python lint (ruff): clean
  • Python format (ruff): clean
  • Frontend lint (biome): clean

Closes #8613

Include session_view.ui_values in the HTML export serialization so
widget states (dropdowns, sliders, plotly selections) are preserved
instead of resetting to defaults.

Changes:
- Add ui_values field to NotebookSessionV1 schema (Python, YAML, TS)
- Serialize ui_values in serialize_session_view()
- Hydrate UI_ELEMENT_REGISTRY from ui_values in static mode (mount.tsx)

The frontend hydration mirrors the existing pattern in handleKernelReady
for live session reconnects (handlers.ts:162-166).

Backward compatible: ui_values is Optional/nullable, so old HTML exports
without it continue to work (widgets fall back to data-initial-value).

Closes marimo-team#8613
@vercel

vercel Bot commented Mar 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marimo-docs Ready Ready Preview, Comment Mar 31, 2026 1:47am

Request Review

@dondetir

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

@github-actions

github-actions Bot commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@dmadisetti

Copy link
Copy Markdown
Member

@peter-gy I think you had an idea for propagating UI element values?
Surprisingly light PR @dondetir is this still under construction? Things may get hairier soon

@dondetir

Copy link
Copy Markdown
Author

Thanks @dmadisetti! Yes, still a draft — wanted to get the approach in front of you early rather than build out in isolation.

The current change is intentionally minimal: serialize session_view.ui_values into the HTML export and hydrate UI_ELEMENT_REGISTRY on the frontend before render — mirroring what handleKernelReady already does for live session reconnects.

I stress-tested the edge cases from #8613:

  • Dropdowns, sliders, text inputs, batch/form/array — values round-trip correctly through JSON serialization
  • Plotly selection/lasso — preserved (stored in ui_values). Legend visibility is client-side Plotly.js state and was never in ui_values — out of scope here
  • File browser — selected files are restored, but browsing still needs a live kernel (pre-existing limitation of static export)
  • TiminginitStore() runs synchronously before root.render(), so the registry is populated before any widget mounts
  • Backward compat — field is Optional/nullable, old exports without it work fine

That said, I expect there are dimensions I haven't considered — happy to hear what @peter-gy had in mind for propagating UI element values. If there's a broader approach this should fit into, I'm glad to adapt or pivot.

Keeping as draft until we're aligned.

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

This pull request has been automatically marked as stale because it has not had activity in 30 days. It will be closed in 14 days if no further activity occurs. If this PR is still relevant, please leave a comment or push new changes to keep it open. Thank you for your contribution!

@github-actions github-actions Bot added the stale label Jul 4, 2026
@dondetir

Copy link
Copy Markdown
Author

Still active. Before building this out further: @peter-gy, @dmadisetti mentioned you had an idea for propagating UI element values. Want to align on the hydration approach before I extend the draft. Current approach: serialize session_view.ui_values into the export HTML and hydrate UI_ELEMENT_REGISTRY before render. Can adapt if you have a different design in mind.

@github-actions github-actions Bot removed the stale label Jul 10, 2026
@peter-gy

Copy link
Copy Markdown
Collaborator

Hi @dondetir! Thanks for exploring this!

The approach looks pragmatic for this case, but we’re still working through the broader widget persistence story before committing to a direction in core. In the meantime, trrackpy by @kirangadhave (https://github.com/kirangadhave/trrackpy/blob/main/packages/trrack-widget/README.md?plain=1#L64-L83) may be worth a look for persisting widget state without changes to core marimo itself.

@manzt

manzt commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Closing this for now while we work through the broader widget-persistence design. We’ll use this PR as a reference.

@manzt manzt closed this Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Export as HTML doesn't preserve control states

4 participants