What's Changed
This release brings a unified data table explorer, reliability improvements to the programmatic notebook API that power the new marimo-pair agent skill, smarter numeric formatting in tables, faster mo.persistent_cache, and a contextual tips system in the CLI.
⭐ Highlights
Combined row viewer and column explorer
The row viewer and column explorer panels are now unified into a single tabbed "Table Explorer" pane. A single toolbar button opens and closes the panel; Rows and Columns tabs live inside it, and your selected tab persists across open/close.
Pair programming with marimo-pair
The experimental _code API receives reliability fixes in this release, enabling the new marimo-pair agent skill for pair programming in marimo notebooks.
npx skills add marimo-team/marimo-pair🚨 Breaking changes
mo.image no longer normalizes uint8 values (#8889)
Previously, mo.image() normalized all numeric arrays (including uint8) to the [0, 1] float range. Now, uint8 arrays are always rendered with values in [0, 255] without normalization. Two new parameters — vmin and vmax — let you set explicit value bounds for under- or over-saturated displays. If you relied on the old uint8 normalization, pass vmin=0, vmax=1 explicitly.
__marimo__ location now follows sys.pycache_prefix (#8797)
The __marimo__ directory now respects sys.pycache_prefix, consistent with Python's own __pycache__ placement. This also fixes cache placement for notebooks in nested directories. Existing caches will not be migrated — they can be safely deleted.
Cache version bump (#8793)
The cache format version has been bumped, invalidating existing caches.
✨ Enhancements
- Remove auto-instantiate from /api/execute endpoint (#8943)
- Use document as source of truth in code_mode _apply_ops (#8944)
- Enhance SQLAlchemy engine with safe_execute and inspector methods for SnowFlake (#8920)
- Support custom cloudpathlib providers in path normalization (#8929)
- Use variable name as download filename in dataframe viewer (#8811)
- Unify row viewer and column explorer (#8905)
- Fix
hide_codenot taking effect on kernel-created cells (#8926) - Virtualize data table rows when pagination is disabled (#8899)
- Emit document transactions from
--watchfile reload (#8846) - Remove document mutation from
session.notify()(#8886) - Style fix for li & ol: reduce margin and restore original disc (#8768)
- Avoid selecting cells in table when interactive elements (#8862)
- Lazy-load KaTeX via dynamic import of @streamdown/math (#8874)
- Display startup tips in CLI (#8836)
- Add ListSQLSchemas to support lazy schema fetching in datasource panel (#8824)
- Auto right-align numeric columns and normalize decimal formatting in tables (#8887)
- Mechanism for parallel read/write in mo.persistent_cache (#8805)
- Convert fonts from TTF to WOFF2, remove unused font files (#8873)
- Prefer tomllib over tomlkit for reading TOML (#8827)
- Support markdown in file tree notebook creation (#8770)
- Allow
codeas positional arg inedit_cell(#8806) - Discourage casual cell naming in code mode (#8804)
- Rename check to skip_validation in code mode API (#8803)
- Document stale globals caveat on ctx.globals (#8802)
- Make _CellsView behave like a read-only ordered dict (#8778)
- Downgrade model fallback log from warning to debug (#8773)
- Add snippet buttons in storage file viewer (#8737)
- Isolation when running multiple notebooks in an app server (#8611)
- Pass raw data for tables during search and copy text/html to clipboard (#8622)
- Support dragging range slider track to move entire range (#8698)
- Propagate notebook
__doc__to cell execution namespace (#8636) - Add POST /api/kernel/focus_cell endpoint for external editor integration (#8497)
🐛 Bug fixes
- Fix
_code_modecell ID collisions on large notebooks (#8951) - Support datetime values in
mo.ui.matplotlibselection masks (#8940) - Sanitize password in frontend render (#8857)
- Kill LSP child processes on shutdown to prevent memory leak (#8927)
- Replace lodash imports with built-in or custom (#8878)
- Remove Content-Length from virtual file StreamingResponse (#8928)
- Enforce cell_ids parameter in session serialization and caching (#8904)
- File descriptor conflicts in terminal ws (#8896)
- Remove matplotlib stretch effect in
mo.ui.matplotlib(#8883) - Key pending tasks by event loop (#8875)
- Use decoding as a heuristic for binary data, and download accordingly (#8858)
- Add script dependencies to data_editor example (#8867)
- Fix cached self entries fsspec (#8863)
- Provide a warning when toml keys are skipped for security (#8854)
- Set file in compilation for app mode linecache and better stack traces (#8800)
- Use python -m pip instead of pip --python for PipPackageManager (#8840)
- Remove litellm (#8852)
- Render data-tooltip in portal to prevent clipping in overflow containers (#8813)
- Clamp memory reported in cgroup v1 reports (#8841)
- Fix asyncio tasks starved by blocking control loop (#8825)
- Keep markdown preview in sync on local edits (#8832)
- Stop mac completion bindings from stealing backticks (#8829)
- Ignore blank line rules (E302, E305) (#8815)
- Fix cells stuck as "needs run" after backend-initiated execution (#8794)
- Graceful fallback for unsupported extension dtypes in to_arrow_ipc (#8785)
- Harden conversion path for ipynbs (#8795)
- Fix console output routing for code_mode run_cell (#8790)
- Append .py extension to markdown notebooks for correct ruff formatting (#8786)
- Patch html-to-image for PNG export font crash and tainted canvas (#8754)
- Exclude index columns for pivot for count/sum agg (#8769)
- Markdown bullets inside of details (#8767)
- Make the pickling mechanism in mo.cache more robust (#8761)
- Recursive private function not detected (#8762)
- Fallback static parsing for completely invalid notebooks (#8723)
- Fix Ty LSP startup and stale diagnostics in notebook editor (#8390)
- Slider editable input ignores debounce bug (#8682)
- Fix three issues with
mo.mpl.interactiveinmarimo runmode (#8760)
📚 Documentation
- Replace white background in NumFOCUS logo with transparency (#8812)
- Add markdown support for documentation with edge middleware and conversion scripts (#8796)
🔬 Preview features
- Code mode API improvements enabling marimo-pair (#8951, #8944, #8943)
📝 Other changes
- Refactor session extensions to use EventAwareExtension base class (#8678)
- Update dependency yaml to v2.8.3 [security] (#8902)
- Update dependency path-to-regexp to v8.4.0 [security] (#8913)
- Bump yaml from 1.10.2 to 2.8.3 (#8897)
- Update dependency yaml to v2.8.3 [security] (#8891)
- Lint configuration (#8560)
- Make transitionCell pure by injecting parseOutline (#8729)
- Add new
NotebookDocumentmodel (#8842)
Contributors
Thanks to all our community and contributors who made this release possible: @abhiyadav2345, @akshayka, @app/dependabot, @app/marimo-github-maintenance-bot, @app/renovate, @Bortlesboat, @daizutabi, @dmadisetti, @kirangadhave, @koaning, @Light2Dark, @manzt, @mauro-cerzosimo, @mscolnick, @peter-gy, @Sushit-prog, @tomneep
And especially to our new contributors:
- @Bortlesboat made their first contribution in #8785
- @tomneep made their first contribution in #8867
Full Changelog: 0.21.1...0.22.0