marimo https://docs.marimo.io marimo is an [open-source](https://github.com/marimo-team/marimo) reactive Python notebook: run a cell or interact with a UI element, and marimo automatically runs dependent cells (or [marks them as stale](https://docs.marimo.io/guides/reactivity/#configuring-how-marimo-runs-cells)), keeping code and outputs consistent and preventing bugs before they happen. Every marimo notebook is stored as pure Python (Git-friendly), executable as a script, and deployable as an app; while stored as Python, marimo notebooks also have native support for SQL. install with pipinstall with uvinstall with conda `[](#__codelineno-0-1)pip install marimo && marimo tutorial intro` `[](#__codelineno-1-1)uv add marimo && uv run marimo tutorial intro` `[](#__codelineno-2-1)conda install -c conda-forge marimo && marimo tutorial intro` Developer experience is core to marimo, with an emphasis on reproducibility, maintainability, composability, and shareability. Highlights[¶](#highlights "Permanent link") ------------------------------------------- * 🚀 **batteries-included:** replaces `jupyter`, `streamlit`, `jupytext`, `ipywidgets`, `papermill`, and more * ⚡️ **reactive**: run a cell, and marimo reactively [runs all dependent cells](https://docs.marimo.io/guides/reactivity/) or [marks them as stale](#expensive-notebooks) * 🖐️ **interactive:** [bind sliders, tables, plots, and more](https://docs.marimo.io/guides/interactivity/) to Python — no callbacks required * 🐍 **git-friendly:** stored as `.py` files * 🛢️ **designed for data**: query dataframes, databases, warehouses, and lakehouses [with SQL](https://docs.marimo.io/guides/working_with_data/sql/); filter and search [dataframes](https://docs.marimo.io/guides/working_with_data/dataframes/) * 🤖 **AI-native**: [generate cells with AI](https://docs.marimo.io/guides/generate_with_ai/) tailored for data work * 🔬 **reproducible:** [no hidden state](https://docs.marimo.io/guides/reactivity/), deterministic execution, [built-in package management](https://docs.marimo.io/guides/editor_features/package_management/) * 🏃 **executable:** [execute as a Python script](https://docs.marimo.io/guides/scripts/), parameterized by CLI args * 🛜 **shareable**: [deploy as an interactive web app](https://docs.marimo.io/guides/apps/) or [slides](https://docs.marimo.io/guides/apps/#slides-layout), [run in the browser via WASM](https://docs.marimo.io/guides/wasm/) * 🧩 **reusable:** [import functions and classes](https://docs.marimo.io/guides/reusing_functions/) from one notebook to another * 🧪 **testable:** [run pytest](https://docs.marimo.io/guides/testing/) on notebooks * ⌨️ **a modern editor**: [GitHub Copilot](https://docs.marimo.io/guides/editor_features/ai_completion/#github-copilot), [AI assistants](https://docs.marimo.io/guides/editor_features/ai_completion/), vim keybindings, variable explorer, and [more](https://docs.marimo.io/guides/editor_features/) A reactive programming environment[¶](#a-reactive-programming-environment "Permanent link") ------------------------------------------------------------------------------------------- marimo guarantees your notebook code, outputs, and program state are consistent. This [solves many problems](https://docs.marimo.io/faq/#faq-problems) associated with traditional notebooks like Jupyter. **A reactive programming environment.** Run a cell and marimo _reacts_ by automatically running the cells that reference its variables, eliminating the error-prone task of manually re-running cells. Delete a cell and marimo scrubs its variables from program memory, eliminating hidden state. **Compatible with expensive notebooks.** marimo lets you [configure the runtime to be lazy](https://docs.marimo.io/guides/configuration/runtime_configuration/), marking affected cells as stale instead of automatically running them. This gives you guarantees on program state while preventing accidental execution of expensive cells. **Synchronized UI elements.** Interact with [UI elements](https://docs.marimo.io/guides/interactivity/) like [sliders](https://docs.marimo.io/api/inputs/slider/#slider), [dropdowns](https://docs.marimo.io/api/inputs/dropdown/), [dataframe transformers](https://docs.marimo.io/api/inputs/dataframe/), and [chat interfaces](https://docs.marimo.io/api/inputs/chat/), and the cells that use them are automatically re-run with their latest values. **Interactive dataframes.** [Page through, search, filter, and sort](https://docs.marimo.io/guides/working_with_data/dataframes/) millions of rows blazingly fast, no code required. **Generate cells with data-aware AI.** [Generate code with an AI assistant](https://docs.marimo.io/guides/editor_features/ai_completion/) that is highly specialized for working with data, with context about your variables in memory; [zero-shot entire notebooks](https://docs.marimo.io/guides/generate_with_ai/text_to_notebook/). Customize the system prompt, bring your own API keys, or use local models. **Query data with SQL.** Build [SQL](https://docs.marimo.io/guides/working_with_data/sql.html) queries that depend on Python values and execute them against dataframes, databases, lakehouses, CSVs, Google Sheets, or anything else using our built-in SQL engine, which returns the result as a Python dataframe. Your notebooks are still pure Python, even if they use SQL. **Dynamic markdown.** Use markdown parametrized by Python variables to tell dynamic stories that depend on Python data. **Built-in package management.** marimo has built-in support for all major package managers, letting you [install packages on import](https://docs.marimo.io/guides/editor_features/package_management/). marimo can even [serialize package requirements](https://docs.marimo.io/guides/package_management/inlining_dependencies/) in notebook files, and auto install them in isolated venv sandboxes. **Deterministic execution order.** Notebooks are executed in a deterministic order, based on variable references instead of cells' positions on the page. Organize your notebooks to best fit the stories you'd like to tell. **Performant runtime.** marimo runs only those cells that need to be run by statically analyzing your code. **Batteries-included.** marimo comes with GitHub Copilot, AI assistants, Ruff code formatting, HTML export, fast code completion, a [VS Code extension](https://marketplace.visualstudio.com/items?itemName=marimo-team.vscode-marimo), an interactive dataframe viewer, and [many more](https://docs.marimo.io/guides/editor_features/) quality-of-life features. Quickstart[¶](#quickstart "Permanent link") ------------------------------------------- _The [marimo concepts playlist](https://www.youtube.com/watch?v=3N6lInzq5MI&list=PLNJXGo8e1XT9jP7gPbRdm1XwloZVFvLEq) on our [YouTube channel](https://www.youtube.com/@marimo-team) gives an overview of many features._ **Installation.** In a terminal, run `[](#__codelineno-3-1)pip install marimo # or conda install -c conda-forge marimo [](#__codelineno-3-2)marimo tutorial intro` To install with additional dependencies that unlock SQL cells, AI completion, and more, run `[](#__codelineno-4-1)pip install marimo[recommended]` **Create notebooks.** Create or edit notebooks with **Run apps.** Run your notebook as a web app, with Python code hidden and uneditable: `[](#__codelineno-6-1)marimo run your_notebook.py` **Execute as scripts.** Execute a notebook as a script at the command line: **Automatically convert Jupyter notebooks.** Automatically convert Jupyter notebooks to marimo notebooks with the CLI `[](#__codelineno-8-1)marimo convert your_notebook.ipynb > your_notebook.py` or use our [web interface](https://marimo.io/convert). **Tutorials.** List all tutorials: Questions?[¶](#questions "Permanent link") ------------------------------------------ See our [FAQ](https://docs.marimo.io/faq/). Learn more[¶](#learn-more "Permanent link") ------------------------------------------- marimo is easy to get started with, with lots of room for power users. For example, here's an embedding visualizer made in marimo ([video](https://marimo.io/videos/landing/full.mp4)): Check out our [guides](https://docs.marimo.io/guides/), [usage examples](https://docs.marimo.io/examples/), and our [gallery](https://marimo.io/gallery) to learn more.
TutorialInputsPlotsLayout
Contributing[¶](#contributing "Permanent link") ----------------------------------------------- We appreciate all contributions! You don't need to be an expert to help out. Please see [CONTRIBUTING.md](https://github.com/marimo-team/marimo/blob/main/CONTRIBUTING.md) for more details on how to get started. > Questions? Reach out to us [on Discord](https://marimo.io/discord?ref=docs). We're building a community. Come hang out with us! * 🌟 [Star us on GitHub](https://github.com/marimo-team/marimo) * 💬 [Chat with us on Discord](https://marimo.io/discord?ref=docs) * 📧 [Subscribe to our Newsletter](https://marimo.io/newsletter) * ☁️ [Join our Cloud Waitlist](https://marimo.io/cloud) * ✏️ [Start a GitHub Discussion](https://github.com/marimo-team/marimo/discussions) * 💬 [Follow us on Bluesky](https://bsky.app/profile/marimo.io) * 🐦 [Follow us on Twitter](https://twitter.com/marimo_io) * 🎥 [Subscribe on YouTube](https://www.youtube.com/@marimo-team) * 💬 [Follow us on Mastodon](https://mastodon.social/@marimo_io) * 🕴️ [Follow us on LinkedIn](https://www.linkedin.com/company/marimo-io) **A NumFOCUS affiliated project.** marimo is a core part of the broader Python ecosystem and is a member of the NumFOCUS community, which includes projects such as NumPy, SciPy, and Matplotlib. Inspiration ✨[¶](#inspiration "Permanent link") ----------------------------------------------- marimo is a **reinvention** of the Python notebook as a reproducible, interactive, and shareable Python program, instead of an error-prone JSON scratchpad. We believe that the tools we use shape the way we think — better tools, for better minds. With marimo, we hope to provide the Python community with a better programming environment to do research and communicate it; to experiment with code and share it; to learn computational science and teach it. Our inspiration comes from many places and projects, especially [Pluto.jl](https://github.com/fonsp/Pluto.jl), [ObservableHQ](https://observablehq.com/tutorials), and [Bret Victor's essays](http://worrydream.com/). marimo is part of a greater movement toward reactive dataflow programming. From [IPyflow](https://github.com/ipyflow/ipyflow), [streamlit](https://github.com/streamlit/streamlit), [TensorFlow](https://github.com/tensorflow/tensorflow), [PyTorch](https://github.com/pytorch/pytorch/tree/main), [JAX](https://github.com/google/jax), and [React](https://github.com/facebook/react), the ideas of functional, declarative, and reactive programming are transforming a broad range of tools for the better.
Getting Started - marimo https://docs.marimo.io/getting_started/ These tutorials will help you get started with marimo | Guide | Description | | --- | --- | | [Installation](https://docs.marimo.io/getting_started/installation/) | Installing marimo | | [Quickstart](https://docs.marimo.io/getting_started/quickstart/) | Create notebooks, run apps, and more from the marimo command-line | | [Key Concepts](https://docs.marimo.io/getting_started/key_concepts/) | A tour of key features and concepts | User Guide - marimo https://docs.marimo.io/guides/ Guides[¶](#guides "Permanent link") ----------------------------------- These guides cover marimo's core concepts. Learn by doing! Prefer a hands-on learning experience? marimo comes packaged with interactive tutorials that you can launch with `marimo tutorial` at the command line. | Guide | Description | | --- | --- | | [Running cells](https://docs.marimo.io/guides/reactivity/) | Understanding how marimo runs cells | | [Interactive elements](https://docs.marimo.io/guides/interactivity/) | Using interactive UI elements | | [Visualizing outputs](https://docs.marimo.io/guides/outputs/) | Creating markdown, plots, and other visual outputs | | [Migrating from Jupyter](https://docs.marimo.io/guides/coming_from/jupyter/) | Tips for transitioning from Jupyter | | [Expensive notebooks](https://docs.marimo.io/guides/expensive_notebooks/) | Tips for working with expensive notebooks | | [Understanding errors](https://docs.marimo.io/guides/understanding_errors/) | Understanding marimo's constraints on notebook code | | [Working with data](https://docs.marimo.io/guides/working_with_data/) | Using SQL cells, no-code dataframe, and reactive plots | | [Package management](https://docs.marimo.io/guides/package_management/) | Inlining dependencies in notebook files and other package management guides | | [Generate with AI](https://docs.marimo.io/guides/generate_with_ai/) | Generate notebooks with AI | | [Editor features](https://docs.marimo.io/guides/editor_features/) | View variables, dataframe schemas, docstrings, and more | | [Using your own editor](https://docs.marimo.io/guides/editor_features/watching/) | Edit notebooks in your own editor and stream changes back to the browser | | [Apps](https://docs.marimo.io/guides/apps/) | Running notebooks as apps | | [Scripts](https://docs.marimo.io/guides/scripts/) | Running notebooks as scripts | | [Reusing functions and classes](https://docs.marimo.io/guides/reusing_functions/) | Importing functions and classes defined in marimo notebooks | | [Tests](https://docs.marimo.io/guides/testing/) | Running unit tests in notebooks | | [Export notebooks](https://docs.marimo.io/guides/exporting/) | Exporting notebooks to HTML, ipynb, flat scripts, and more | | [Publish to the web](https://docs.marimo.io/guides/publishing/) | Edit and publish notebooks on the web | | [Run notebooks with WebAssembly](https://docs.marimo.io/guides/wasm/) | Create notebooks in our online playground | | [Deploying](https://docs.marimo.io/guides/deploying/) | Deploying marimo notebooks and apps | | [Configuration](https://docs.marimo.io/guides/configuration/) | Configure various settings | | [Coming from other tools](https://docs.marimo.io/guides/coming_from/) | Transitioning from Jupyter and other tools | | [Extending marimo](https://docs.marimo.io/guides/integrating_with_marimo/) | Rich displays of objects, custom UI plugins | | [State management](https://docs.marimo.io/guides/state/) | Advanced: mutable reactive state | | [Best practices](https://docs.marimo.io/guides/best_practices/) | Best practices to help you get the most out of marimo | | [Troubleshooting](https://docs.marimo.io/guides/troubleshooting/) | Troubleshooting notebooks | Examples - marimo https://docs.marimo.io/examples/ This page includes dozens of bite-sized how-to examples to help you get started with marimo. Be sure to also read the [quickstart](https://docs.marimo.io/getting_started/) and the [user guide](https://docs.marimo.io/guides/), especially the guide on [how marimo runs cells](https://docs.marimo.io/guides/reactivity/). Get inspired at our gallery! For inspirational examples, including embedding-driven data labelers, Stanford-scientist authored tutorials, and more, check out our [public gallery](https://marimo.io/gallery). Running cells[¶](#running-cells "Permanent link") ------------------------------------------------- * ⚡️ [**Basic execution**](https://docs.marimo.io/examples/running_cells/basics/) * 🐞 [**Getting around multiple definition errors**](https://docs.marimo.io/examples/running_cells/multiple_definitions/) * 🛑 [**Stop cells from running**](https://docs.marimo.io/examples/running_cells/stop/) * 🖱️ [**Run cells on button click**](https://docs.marimo.io/examples/running_cells/run_button/) * 🕓 [**Refresh on a timer**](https://docs.marimo.io/examples/running_cells/refresh/) * ⏳ [**Run async functions**](https://docs.marimo.io/examples/running_cells/async_await/) * 💾 [**Caching computations in memory**](https://docs.marimo.io/examples/running_cells/memory_cache/) * 💾 [**Cache computations to persistent storage**](https://docs.marimo.io/examples/running_cells/persistent_cache/) * 🐞 [**Using the debugger**](https://docs.marimo.io/examples/running_cells/debugging/) * 🐍 [**Run notebooks as scripts**](https://docs.marimo.io/guides/scripts/) Visual Outputs[¶](#visual-outputs "Permanent link") --------------------------------------------------- * 📤 [**Cell outputs**](https://docs.marimo.io/examples/outputs/basic_output/) * ✍️ [**Basic markdown**](https://docs.marimo.io/examples/outputs/basic_markdown/) * 💬 [**Console outputs**](https://docs.marimo.io/examples/outputs/console_outputs/) * 📋 [**Capturing console output**](https://docs.marimo.io/examples/outputs/capture_console_outputs/) * 📈 [**Showing plots**](https://docs.marimo.io/examples/outputs/plots/) * 🎥 [**Showing videos and other media**](https://docs.marimo.io/api/media/) * 🎛️ [**Conditionally showing outputs**](https://docs.marimo.io/examples/outputs/conditional_output/) * 🧩 [**Showing multiple outputs in one cell**](https://docs.marimo.io/examples/outputs/multiple_outputs/) ### Writing markdown[¶](#writing-markdown "Permanent link") * ⚡️ [**Python values in markdown**](https://docs.marimo.io/examples/markdown/dynamic_markdown/) * * * * 🪄 [**Mermaid diagrams**](https://docs.marimo.io/examples/markdown/mermaid/) * * * * 🚨 [**Admonitions**](https://docs.marimo.io/examples/markdown/admonitions/) * * * * 📂 [**Collapsible details**](https://docs.marimo.io/examples/markdown/details/) * * * * 😀 [**Emoji**](https://docs.marimo.io/examples/markdown/emoji/) Working with data[¶](#working-with-data "Permanent link") --------------------------------------------------------- ### Dataframes[¶](#dataframes "Permanent link") marimo is designed for working with dataframes. Here are a few examples; see the [dataframes guide](https://docs.marimo.io/guides/working_with_data/dataframes/) for details. * 🧮 [**Interactive dataframe viewer**](https://docs.marimo.io/examples/outputs/dataframes/) * * * * 🔍 [**Select dataframe rows**](https://docs.marimo.io/api/inputs/table/) * * * * ✏️ [**Editable dataframe**](https://docs.marimo.io/api/inputs/data_editor/) * * * * 🛠️ [**Interactive dataframe transformer**](https://docs.marimo.io/api/inputs/dataframe/) * * * ### SQL[¶](#sql "Permanent link") Here are some basic examples, see the [SQL guide](https://docs.marimo.io/guides/working_with_data/sql/) for more details. * 🦆 [**Query dataframes with DuckDB SQL**](https://docs.marimo.io/guides/working_with_data/sql/#example) * 🛢️ [**SQLite, Postgres, and other engines**](https://docs.marimo.io/guides/working_with_data/sql/#connecting-to-a-custom-database) ### Plots[¶](#plots "Permanent link") See the [plotting guide](https://docs.marimo.io/guides/working_with_data/plotting/) for a full overview. * 📊 [**Selecting data with Altair**](https://docs.marimo.io/api/plotting/#reactive-charts-with-altair) * * * * 📉 [**Selecting data with Plotly**](https://docs.marimo.io/guides/working_with_data/plotting/#plotly) * * * * 🔭 [**Showing matplotlib plots**](https://docs.marimo.io/examples/outputs/plots/) ### Progress bars and status elements[¶](#progress-bars-and-status-elements "Permanent link") * 📶 [**Progress bar**](https://docs.marimo.io/examples/outputs/progress_bar/) * * * * 🌀 [**Loading spinner**](https://docs.marimo.io/examples/outputs/spinner/) * * * ### Layouts[¶](#layouts "Permanent link") * 📐 [**Horizontal and vertical stacking**](https://docs.marimo.io/examples/outputs/stacks/) * * * * 📁 [**Accordion toggle**](https://docs.marimo.io/api/layouts/accordion/) * * * * 🗂️ [**Tabs**](https://docs.marimo.io/api/inputs/tabs/) * * * Input elements[¶](#input-elements "Permanent link") --------------------------------------------------- ### Basic input elements[¶](#basic-input-elements "Permanent link") marimo has a large library of interactive UI elements, which you can use without callbacks — just make sure to assign elements to global variables. See the [API reference](https://docs.marimo.io/api/inputs/) for a full list, and the [interactivity guide](https://docs.marimo.io/guides/interactivity/) for rules governing how UI elements work. * 🎚️ [**Slider**](https://docs.marimo.io/api/inputs/slider/) * * * * 🧾 [**Dropdown**](https://docs.marimo.io/api/inputs/dropdown/) * * * * 👆 [**Multi-select**](https://docs.marimo.io/api/inputs/multiselect/) * * * * 🔘 [**Radio buttons**](https://docs.marimo.io/api/inputs/radio/) * * * * ☑️ [**Checkbox**](https://docs.marimo.io/api/inputs/checkbox/) * * * * 📅 [**Date**](https://docs.marimo.io/api/inputs/dates/) * * * * 📁 [**File**](https://docs.marimo.io/api/inputs/file/) * * * * 🔤 [**Text input**](https://docs.marimo.io/api/inputs/text/) * * * * 📝 [**Text area**](https://docs.marimo.io/api/inputs/text_area/) * * * * 🧑‍💻 [**Code editor**](https://docs.marimo.io/api/inputs/code_editor/) * * * * 🔍 [**Table**](https://docs.marimo.io/api/inputs/table/) * * * * 🎙️ [**Microphone**](https://docs.marimo.io/api/inputs/microphone/) * * * * 💬 [**Chat**](https://docs.marimo.io/api/inputs/chat/) * * * ### Composite input elements[¶](#composite-input-elements "Permanent link") Composite input elements let you create a single UI element from multiple other UI elements. * 🧾 [**Form**](https://docs.marimo.io/api/inputs/form/) * * * * 🎒 [**Array**](https://docs.marimo.io/api/inputs/array/) * * * * 📖 [**Dictionary**](https://docs.marimo.io/api/inputs/dictionary/) * * * API Reference - marimo https://docs.marimo.io/api/ Use the marimo library in marimo notebooks (`import marimo as mo`) to * connect interactive inputs like sliders, dropdowns, and tables to Python, * express yourself with dynamically created markdown, * layout information with tabs or grids, * output media like images and audio, * and more! | | | | --- | --- | | [markdown](https://docs.marimo.io/api/markdown/) | Write markdown with `mo.md` | | [inputs](https://docs.marimo.io/api/inputs/) | Connect sliders, dropdowns, tables, and more to Python | | [layouts](https://docs.marimo.io/api/layouts/) | Customize outputs with accordions, tabs, stacks, and more | | [plotting](https://docs.marimo.io/api/plotting/) | Output interactive plots | | [media](https://docs.marimo.io/api/media/) | Output media like images, audio, PDFs, and plain text | | [diagrams](https://docs.marimo.io/api/diagrams/) | Flow charts, graphs, statistic cards, and more | | [status](https://docs.marimo.io/api/status/) | Display progress indicators | | [outputs](https://docs.marimo.io/api/outputs/) | Modify cell outputs, redirect console output | | [control\_flow](https://docs.marimo.io/api/control_flow/) | Control how cells execute | | [html](https://docs.marimo.io/api/html/) | Manipulate HTML objects | | [query\_params](https://docs.marimo.io/api/query_params/) | Access and set query parameters with `mo.query_params` | | [cli\_args](https://docs.marimo.io/api/cli_args/) | Access command-line arguments with `mo.cli_args` | | [caching](https://docs.marimo.io/api/caching/) | Cache expensive computations in memory or on disk | | [state](https://docs.marimo.io/api/state/) | Synchronize multiple UI elements with `mo.state` | | [app](https://docs.marimo.io/api/app/) | Embed notebooks in other notebooks | | [cell](https://docs.marimo.io/api/cell/) | Run cells defined in another notebook | | [watch](https://docs.marimo.io/api/watch/) | Reactively respond to file changes on disk | | [miscellaneous](https://docs.marimo.io/api/miscellaneous/) | Miscellaneous utilities | Commands - marimo https://docs.marimo.io/cli/ Welcome to marimo! Getting started: * marimo tutorial intro Example usage: * marimo edit create or edit notebooks * marimo edit notebook.py create or edit a notebook called notebook.py * marimo run notebook.py run a notebook as a read-only app * marimo tutorial --help list tutorials **Usage:** `[](#__codelineno-0-1)marimo [OPTIONS] COMMAND [ARGS]...` **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `--version` | boolean | Show the version and exit. | `False` | | `-l`, `--log-level` | choice (`DEBUG` | `INFO` | `WARN` | `ERROR` | `CRITICAL`) | Choose logging level. | `WARN` | | `-q`, `--quiet` | boolean | Suppress standard out. | `False` | | `-y`, `--yes` | boolean | Automatic yes to prompts, running non-interactively. | `False` | | `-d`, `--development-mode` | boolean | Run in development mode; enables debug logs and server autoreload. | `False` | | `--help` | boolean | Show this message and exit. | `False` | marimo config[¶](#marimo-config "Permanent link") ------------------------------------------------- Various commands for the marimo config. **Usage:** `[](#__codelineno-1-1)marimo config [OPTIONS] COMMAND [ARGS]...` **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `--help` | boolean | Show this message and exit. | `False` | ### marimo config describe[¶](#marimo-config-describe "Permanent link") Describe the marimo config. **Usage:** `[](#__codelineno-2-1)marimo config describe [OPTIONS]` **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `--help` | boolean | Show this message and exit. | `False` | ### marimo config show[¶](#marimo-config-show "Permanent link") Show the marimo config. **Usage:** `[](#__codelineno-3-1)marimo config show [OPTIONS]` **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `--help` | boolean | Show this message and exit. | `False` | marimo convert[¶](#marimo-convert "Permanent link") --------------------------------------------------- Convert a Jupyter notebook or Markdown file to a marimo notebook. The argument may be either a path to a local .ipynb/.md file, or an .ipynb/.md file hosted on GitHub. Example usage: `marimo convert your_nb.ipynb -o your_nb.py` or `marimo convert your_nb.md -o your_nb.py` Jupyter notebook conversion will strip out all outputs. Markdown cell conversion with occur on the presence of `{python}` code fences. After conversion, you can open the notebook in the editor: Since marimo is different from traditional notebooks, once in the editor, you may need to fix errors like multiple definition errors or cycle errors. **Usage:** `[](#__codelineno-4-1)marimo convert [OPTIONS] FILENAME` **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `-o`, `--output` | path | Output file to save the converted notebook to. If not provided, the converted notebook will be printed to stdout. | None | | `--help` | boolean | Show this message and exit. | `False` | marimo edit[¶](#marimo-edit "Permanent link") --------------------------------------------- Create or edit notebooks. * marimo edit Start the marimo notebook server * marimo edit notebook.py Create or edit notebook.py **Usage:** `[](#__codelineno-5-1)marimo edit [OPTIONS] [NAME] [ARGS]...` **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `-p`, `--port` | integer | Port to attach to. | None | | `--host` | text | Host to attach to. | `127.0.0.1` | | `--proxy` | text | Address of reverse proxy. | None | | `--headless` | boolean | Don't launch a browser. | `False` | | `--token` / `--no-token` | boolean | Use a token for authentication. This enables session-based authentication. A random token will be generated if --token-password is not set. If --no-token is set, session-based authentication will not be used. | `True` | | `--token-password` | text | Use a specific token for authentication. This enables session-based authentication. A random token will be generated if not set. | None | | `--base-url` | text | Base URL for the server. Should start with a /. | \`\` | | `--allow-origins` | text | Allowed origins for CORS. Can be repeated. Use \* for all origins. | None | | `--skip-update-check` | boolean | Don't check if a new version of marimo is available for download. | `False` | | `--sandbox` / `--no-sandbox` | boolean | Run the command in an isolated virtual environment using `uv run --isolated`. Requires `uv`. | None | | `--watch` | boolean | Watch the file for changes and reload the code when saved in another editor. | `False` | | `--skew-protection` / `--no-skew-protection` | boolean | Enable skew protection middleware to prevent version mismatch issues. | `True` | | `--help` | boolean | Show this message and exit. | `False` | marimo env[¶](#marimo-env "Permanent link") ------------------------------------------- Print out environment information for debugging purposes. **Usage:** **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `--help` | boolean | Show this message and exit. | `False` | marimo export[¶](#marimo-export "Permanent link") ------------------------------------------------- Export a notebook to various formats. **Usage:** `[](#__codelineno-7-1)marimo export [OPTIONS] COMMAND [ARGS]...` **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `--help` | boolean | Show this message and exit. | `False` | ### marimo export html[¶](#marimo-export-html "Permanent link") Run a notebook and export it as an HTML file. Example: `marimo export html notebook.py -o notebook.html` Optionally pass CLI args to the notebook: `marimo export html notebook.py -o notebook.html -- -arg1 foo -arg2 bar` **Usage:** `[](#__codelineno-8-1)marimo export html [OPTIONS] NAME [ARGS]...` **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `--include-code` / `--no-include-code` | boolean | Include notebook code in the exported HTML file. | `True` | | `--watch` / `--no-watch` | boolean | Watch notebook for changes and regenerate the output on modification. If watchdog is installed, it will be used to watch the file. Otherwise, file watcher will poll the file every 1s. | `False` | | `-o`, `--output` | path | Output file to save the HTML to. If not provided, the HTML will be printed to stdout. | None | | `--sandbox` / `--no-sandbox` | boolean | Run the command in an isolated virtual environment using `uv run --isolated`. Requires `uv`. | None | | `--help` | boolean | Show this message and exit. | `False` | ### marimo export html-wasm[¶](#marimo-export-html-wasm "Permanent link") Export a notebook as a WASM-powered standalone HTML file. Example: `marimo export html-wasm notebook.py -o notebook.wasm.html` The exported HTML file will run the notebook using WebAssembly, making it completely self-contained and executable in the browser. This lets you share interactive notebooks on the web without setting up infrastructure to run Python code. The exported notebook runs using Pyodide, which supports most but not all Python packages. To learn more, see the Pyodide documentation. In order for this file to be able to run, it must be served over HTTP, and cannot be opened directly from the file system (e.g. file://). **Usage:** `[](#__codelineno-9-1)marimo export html-wasm [OPTIONS] NAME` **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `-o`, `--output` | path | Output directory to save the HTML to. | \_required | | `--mode` | choice (`edit` | `run`) | Whether the notebook code should be editable or readonly. | `run` | | `--watch` / `--no-watch` | boolean | Whether to watch the original file and export upon change | `False` | | `--show-code` / `--no-show-code` | boolean | Whether to show code by default in the exported HTML file; only relevant for run mode. | `False` | | `--include-cloudflare` / `--no-include-cloudflare` | boolean | Whether to include Cloudflare Worker configuration files (index.js and wrangler.jsonc) for easy deployment. | `False` | | `--sandbox` / `--no-sandbox` | boolean | Run the command in an isolated virtual environment using `uv run --isolated`. Requires `uv`. | None | | `--help` | boolean | Show this message and exit. | `False` | ### marimo export ipynb[¶](#marimo-export-ipynb "Permanent link") Export a marimo notebook as a Jupyter notebook in topological order. Example: `marimo export ipynb notebook.py -o notebook.ipynb` Watch for changes and regenerate the script on modification: `marimo export ipynb notebook.py -o notebook.ipynb --watch` Requires nbformat to be installed. **Usage:** `[](#__codelineno-10-1)marimo export ipynb [OPTIONS] NAME` **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `--sort` | choice (`top-down` | `topological`) | Sort cells top-down or in topological order. | `topological` | | `--watch` / `--no-watch` | boolean | Watch notebook for changes and regenerate the output on modification. If watchdog is installed, it will be used to watch the file. Otherwise, file watcher will poll the file every 1s. | `False` | | `-o`, `--output` | path | Output file to save the ipynb file to. If not provided, the ipynb contents will be printed to stdout. | None | | `--include-outputs` / `--no-include-outputs` | boolean | Run the notebook and include outputs in the exported ipynb file. | `False` | | `--sandbox` / `--no-sandbox` | boolean | Run the command in an isolated virtual environment using `uv run --isolated`. Requires `uv`. | None | | `--help` | boolean | Show this message and exit. | `False` | ### marimo export md[¶](#marimo-export-md "Permanent link") Export a marimo notebook as a code fenced Markdown file. Example: `marimo export md notebook.py -o notebook.md` Watch for changes and regenerate the script on modification: `marimo export md notebook.py -o notebook.md --watch` **Usage:** `[](#__codelineno-11-1)marimo export md [OPTIONS] NAME` **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `--watch` / `--no-watch` | boolean | Watch notebook for changes and regenerate the output on modification. If watchdog is installed, it will be used to watch the file. Otherwise, file watcher will poll the file every 1s. | `False` | | `-o`, `--output` | path | Output file to save the markdown to. If not provided, markdown will be printed to stdout. | None | | `--sandbox` / `--no-sandbox` | boolean | Run the command in an isolated virtual environment using `uv run --isolated`. Requires `uv`. | None | | `--help` | boolean | Show this message and exit. | `False` | ### marimo export script[¶](#marimo-export-script "Permanent link") Export a marimo notebook as a flat script, in topological order. Example: `marimo export script notebook.py -o notebook.script.py` Watch for changes and regenerate the script on modification: `marimo export script notebook.py -o notebook.script.py --watch` **Usage:** `[](#__codelineno-12-1)marimo export script [OPTIONS] NAME` **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `--watch` / `--no-watch` | boolean | Watch notebook for changes and regenerate the output on modification. If watchdog is installed, it will be used to watch the file. Otherwise, file watcher will poll the file every 1s. | `False` | | `-o`, `--output` | path | Output file to save the script to. If not provided, the script will be printed to stdout. | None | | `--sandbox` / `--no-sandbox` | boolean | Run the command in an isolated virtual environment using `uv run --isolated`. Requires `uv`. | None | | `--help` | boolean | Show this message and exit. | `False` | marimo new[¶](#marimo-new "Permanent link") ------------------------------------------- Create an empty notebook, or generate from a prompt with AI * marimo new Create an empty notebook * marimo new "Plot an interactive 3D surface with matplotlib." Generate a notebook from a prompt. * marimo new prompt.txt Generate a notebook from a file containing a prompt. Visit [https://marimo.app/ai](https://marimo.app/ai) for more prompt examples. **Usage:** `[](#__codelineno-13-1)marimo new [OPTIONS] [PROMPT]` **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `-p`, `--port` | integer | Port to attach to. | None | | `--host` | text | Host to attach to. | `127.0.0.1` | | `--proxy` | text | Address of reverse proxy. | None | | `--headless` | boolean | Don't launch a browser. | `False` | | `--token` / `--no-token` | boolean | Use a token for authentication. This enables session-based authentication. A random token will be generated if --token-password is not set. If --no-token is set, session-based authentication will not be used. | `True` | | `--token-password` | text | Use a specific token for authentication. This enables session-based authentication. A random token will be generated if not set. | None | | `--base-url` | text | Base URL for the server. Should start with a /. | \`\` | | `--sandbox` / `--no-sandbox` | boolean | Run the command in an isolated virtual environment using `uv run --isolated`. Requires `uv`. | None | | `--skew-protection` / `--no-skew-protection` | boolean | Enable skew protection middleware to prevent version mismatch issues. | `True` | | `--help` | boolean | Show this message and exit. | `False` | marimo recover[¶](#marimo-recover "Permanent link") --------------------------------------------------- Recover a marimo notebook from JSON. **Usage:** `[](#__codelineno-14-1)marimo recover [OPTIONS] NAME` **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `--help` | boolean | Show this message and exit. | `False` | marimo run[¶](#marimo-run "Permanent link") ------------------------------------------- Run a notebook as an app in read-only mode. If NAME is a url, the notebook will be downloaded to a temporary file. Example: **Usage:** `[](#__codelineno-15-1)marimo run [OPTIONS] NAME [ARGS]...` **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `-p`, `--port` | integer | Port to attach to. | None | | `--host` | text | Host to attach to. | `127.0.0.1` | | `--proxy` | text | Address of reverse proxy. | None | | `--headless` | boolean | Don't launch a browser. | `False` | | `--token` / `--no-token` | boolean | Use a token for authentication. This enables session-based authentication. A random token will be generated if --token-password is not set. If --no-token is set, session-based authentication will not be used. | `False` | | `--token-password` | text | Use a specific token for authentication. This enables session-based authentication. A random token will be generated if not set. | None | | `--include-code` | boolean | Include notebook code in the app. | `False` | | `--session-ttl` | integer | Seconds to wait before closing a session on websocket disconnect. | `120` | | `--watch` | boolean | Watch the file for changes and reload the app. If watchdog is installed, it will be used to watch the file. Otherwise, file watcher will poll the file every 1s. | `False` | | `--skew-protection` / `--no-skew-protection` | boolean | Enable skew protection middleware to prevent version mismatch issues. | `True` | | `--base-url` | text | Base URL for the server. Should start with a /. | \`\` | | `--allow-origins` | text | Allowed origins for CORS. Can be repeated. | None | | `--redirect-console-to-browser` | boolean | Redirect console logs to the browser console. | `False` | | `--sandbox` / `--no-sandbox` | boolean | Run the command in an isolated virtual environment using `uv run --isolated`. Requires `uv`. | None | | `--help` | boolean | Show this message and exit. | `False` | marimo shell-completion[¶](#marimo-shell-completion "Permanent link") --------------------------------------------------------------------- Install shell completions for marimo. Supports bash, zsh, and fish. **Usage:** `[](#__codelineno-16-1)marimo shell-completion [OPTIONS]` **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `--help` | boolean | Show this message and exit. | `False` | marimo tutorial[¶](#marimo-tutorial "Permanent link") ----------------------------------------------------- Open a tutorial. marimo is a powerful library for making reactive notebooks and apps. To get the most out of marimo, get started with a few tutorials, starting with the intro: Recommended sequence: `- intro - dataflow - ui - markdown - plots - sql - layout - fileformat - markdown-format - for-jupyter-users` **Usage:** `[](#__codelineno-17-1)marimo tutorial [OPTIONS] {intro|dataflow|ui|markdown|plots|sql|layout|filefor [](#__codelineno-17-2) mat|markdown-format|for-jupyter-users}` **Options:** | Name | Type | Description | Default | | --- | --- | --- | --- | | `-p`, `--port` | integer | Port to attach to. | None | | `--host` | text | Host to attach to. | `127.0.0.1` | | `--proxy` | text | Address of reverse proxy. | None | | `--headless` | boolean | Don't launch a browser. | `False` | | `--token` / `--no-token` | boolean | Use a token for authentication. This enables session-based authentication. A random token will be generated if --token-password is not set. If --no-token is set, session-based authentication will not be used. | `True` | | `--token-password` | text | Use a specific token for authentication. This enables session-based authentication. A random token will be generated if not set. | None | | `--skew-protection` / `--no-skew-protection` | boolean | Enable skew protection middleware to prevent version mismatch issues. | `True` | | `--help` | boolean | Show this message and exit. | `False` | FAQ - marimo https://docs.marimo.io/faq/ Choosing marimo[¶](#choosing-marimo "Permanent link") ----------------------------------------------------- ### How is marimo different from Jupyter?[¶](#how-is-marimo-different-from-jupyter "Permanent link") marimo is a reinvention of the Python notebook as a reproducible, interactive, and shareable Python program that can be executed as scripts or deployed as interactive web apps. **Consistent state.** In marimo, your notebook code, outputs, and program state are guaranteed to be consistent. Run a cell and marimo reacts by automatically running the cells that reference its variables. Delete a cell and marimo scrubs its variables from program memory, eliminating hidden state. **Built-in interactivity.** marimo also comes with [UI elements](https://docs.marimo.io/guides/interactivity/) like sliders, a dataframe transformer, and interactive plots that are automatically synchronized with Python. Interact with an element and the cells that use it are automatically re-run with its latest value. **Pure Python programs.** Unlike Jupyter notebooks, marimo notebooks are stored as pure Python files that can be executed as scripts, deployed as interactive web apps, and versioned easily with Git. ### What problems does marimo solve?[¶](#what-problems-does-marimo-solve "Permanent link") marimo solves problems in reproducibility, maintainability, interactivity, reusability, and shareability of notebooks. **Reproducibility.** In Jupyter notebooks, the code you see doesn't necessarily match the outputs on the page or the program state. If you delete a cell, its variables stay in memory, which other cells may still reference; users can execute cells in arbitrary order. This leads to widespread reproducibility issues. [One study](https://blog.jetbrains.com/datalore/2020/12/17/we-downloaded-10-000-000-jupyter-notebooks-from-github-this-is-what-we-learned/#consistency-of-notebooks) analyzed 10 million Jupyter notebooks and found that 36% of them weren't reproducible. In contrast, marimo guarantees that your code, outputs, and program state are consistent, eliminating hidden state and making your notebook reproducible. marimo achieves this by intelligently analyzing your code and understanding the relationships between cells, and automatically re-running cells as needed. In addition, marimo notebooks can serialize package requirements inline; marimo runs these "sandboxed" notebooks in temporary virtual environments, making them [reproducible down to the packages](https://docs.marimo.io/guides/editor_features/package_management/). **Maintainability.** marimo notebooks are stored as pure Python programs (`.py` files). This lets you version them with Git; in contrast, Jupyter notebooks are stored as JSON and require extra steps to version. **Interactivity.** marimo notebooks come with [UI elements](https://docs.marimo.io/guides/interactivity/) that are automatically synchronized with Python (like sliders, dropdowns); _eg_, scrub a slider and all cells that reference it are automatically re-run with the new value. This is difficult to get working in Jupyter notebooks. **Reusability.** marimo notebooks can be executed as Python scripts from the command-line (since they're stored as `.py` files). In contrast, this requires extra steps to do for Jupyter, such as copying and pasting the code out or using external frameworks. We also let you import symbols (functions, classes) defined in a marimo notebook into other Python programs/notebooks, something you can't easily do with Jupyter. **Shareability.** Every marimo notebook can double as an interactive web app, complete with UI elements, which you can serve using the `marimo run` command. This isn't possible in Jupyter without substantial extra effort. _To learn more about problems with traditional notebooks, see these references [\[1\]](https://austinhenley.com/pubs/Chattopadhyay2020CHI_NotebookPainpoints.pdf) [\[2\]](https://www.youtube.com/watch?v=7jiPeIFXb6U&t=1s)._ ### How is `marimo.ui` different from Jupyter widgets?[¶](#how-is-marimoui-different-from-jupyter-widgets "Permanent link") Unlike Jupyter widgets, marimo's interactive elements are automatically synchronized with the Python kernel: no callbacks, no observers, no manually re-running cells. Using marimo[¶](#using-marimo "Permanent link") ----------------------------------------------- ### Is marimo a notebook or a library?[¶](#is-marimo-a-notebook-or-a-library "Permanent link") marimo is both a notebook and a library. * Create _marimo notebooks_ with the editor that opens in your browser when you run `marimo edit`. * Use the _marimo library_ (`import marimo as mo`) in marimo notebooks. Write markdown with `mo.md(...)`, create stateful interactive elements with `mo.ui` (`mo.ui.slider(...)`), and more. See the docs for an [API reference](https://docs.marimo.io/api/). ### What's the difference between a marimo notebook and a marimo app?[¶](#whats-the-difference-between-a-marimo-notebook-and-a-marimo-app "Permanent link") marimo programs are notebooks, apps, or both, depending on how you use them. There are two ways to interact with a marimo program: 1. open it as a computational _notebook_ with `marimo edit` 2. run it as an interactive _app_ with `marimo run` All marimo programs start as notebooks, since they are created with `marimo edit`. Because marimo notebooks are reactive and have built-in interactive elements, many can easily be made into useful and beautiful apps by simply hiding the notebook code: this is what `marimo run` does. Not every notebook needs to be run as an app — marimo notebooks are useful in and of themselves for rapidly exploring data and doing reproducible science. And not every app is improved by interacting with the notebook. In some settings, such as collaborative research, education, and technical presentations, going back and forth between the notebook view and app view (which you can do from `marimo edit`) can be useful! ### How does marimo know what cells to run?[¶](#how-does-marimo-know-what-cells-to-run "Permanent link") marimo reads each cell once to determine what global names it defines and what global names it reads. When a cell is run, marimo runs all other cells that read any of the global names it defines. A global name can refer to a variable, class, function, or import. In other words, marimo uses _static analysis_ to make a dataflow graph out of your cells. Each cell is a node in the graph across which global variables "flow". Whenever a cell is run, either because you changed its code or interacted with a UI element it reads, all its descendants run in turn. ### Does marimo slow my code down?[¶](#does-marimo-slow-my-code-down "Permanent link") No, marimo doesn't slow your code down. marimo determines the dependencies among cells by reading your code, not running or tracing it, so there's zero runtime overhead. ### How do I prevent automatic execution from running expensive cells?[¶](#how-do-i-prevent-automatic-execution-from-running-expensive-cells "Permanent link") Reactive (automatic) execution ensures your code and outputs are always in sync, improving reproducibility by eliminating hidden state and out-of-order execution; marimo also takes care to run only the minimal set of cells needed to keep your notebook up to date. But when some cells take a long time to run, it's understandable to be concerned that automatic execution will kick off expensive cells before you're ready to run them. _Here are some tips to avoid accidental execution of expensive cells:_ * [Disable expensive cells](https://docs.marimo.io/guides/reactivity/#disabling-cells). When a cell is disabled, it and its descendants are blocked from running. * Wrap UI elements in a [form](https://docs.marimo.io/api/inputs/form/#marimo.ui.form " marimo.ui.form"). * Use [`mo.stop`](https://docs.marimo.io/api/control_flow/#marimo.stop " marimo.stop") to conditionally stop execution of a cell and its descendants. * Decorate functions with marimo's [`mo.cache`](https://docs.marimo.io/api/caching/#marimo.cache " marimo.cache") to cache expensive intermediate computations. * Use [`mo.persistent_cache`](https://docs.marimo.io/api/caching/#marimo.persistent_cache " marimo.persistent_cache") to cache variables to disk; on re-run, marimo will read values from disk instead of recalculating them as long as the cell is not stale. * Disable automatic execution in the [runtime configuration](https://docs.marimo.io/guides/configuration/runtime_configuration/). ### How do I disable automatic execution?[¶](#how-do-i-disable-automatic-execution "Permanent link") You can disable automatic execution through the notebook runtime settings; see the [guide on runtime configuration](https://docs.marimo.io/guides/configuration/runtime_configuration/). When automatic execution is disabled, marimo still gives you guarantees on your notebook state and automatically marks cells as stale when appropriate. ### How do I use sliders and other interactive elements?[¶](#how-do-i-use-sliders-and-other-interactive-elements "Permanent link") Interactive UI elements like sliders are available in `marimo.ui`. * Assign the UI element to a global variable (`slider = mo.ui.slider(0, 100)`) * Include it in the last expression of a cell to display it (`slider` or `mo.md(f"Choose a value: {slider}")`) * Read its current value in another cell via its `value` attribute (`slider.value`) _When a UI element bound to a global variable is interacted with, all cells referencing the global variable are run automatically_. If you have many UI elements or don't know the elements you'll create until runtime, use `marimo.ui.array` and `marimo.ui.dictionary` to create UI elements that wrap other UI elements (`sliders = mo.ui.array([slider(1, 100) for _ in range(n_sliders)])`). All this and more is explained in the UI tutorial. Run it with at the command line. ### How do I add a submit button to UI elements?[¶](#how-do-i-add-a-submit-button-to-ui-elements "Permanent link") Use the `form` method to add a submit button to a UI element. For example, `[](#__codelineno-1-1)form = marimo.ui.text_area().form()` When wrapped in a form, the text area's value will only be sent to Python when you click the submit button. Access the last submitted value of the text area with `form.value`. ### How do I write markdown?[¶](#how-do-i-write-markdown "Permanent link") Import `marimo` (as `mo`) in a notebook, and use the `mo.md` function. Learn more in the [outputs guide](https://docs.marimo.io/guides/outputs/#markdown) or by running `marimo tutorial markdown`. ### How do I display plots?[¶](#how-do-i-display-plots "Permanent link") Include plots in the last expression of a cell to display them, just like all other outputs. If you're using matplotlib, you can display the `Figure` object (get the current figure with `plt.gcf()`). For examples, run the plots tutorial: Also see the [plotting API reference](https://docs.marimo.io/api/plotting/). ### How do I prevent matplotlib plots from being cut off?[¶](#how-do-i-prevent-matplotlib-plots-from-being-cut-off "Permanent link") If your legend or axes labels are cut off, try calling `plt.tight_layout()` before outputting your plot: `[](#__codelineno-3-1)import matplotlib.pyplot as plt [](#__codelineno-3-2)[](#__codelineno-3-3)plt.plot([-8, 8]) [](#__codelineno-3-4)plt.ylabel("my variable") [](#__codelineno-3-5)plt.tight_layout() [](#__codelineno-3-6)plt.gca()` ### How do I display interactive matplotlib plots?[¶](#how-do-i-display-interactive-matplotlib-plots "Permanent link") Use [`marimo.mpl.interactive`](https://docs.marimo.io/api/plotting/#marimo.mpl.interactive " marimo.mpl.interactive"). `[](#__codelineno-4-1)fig, ax = plt.subplots() [](#__codelineno-4-2)ax.plot([1, 2]) [](#__codelineno-4-3)mo.mpl.interactive(ax)` ### How do I display objects in rows and columns?[¶](#how-do-i-display-objects-in-rows-and-columns "Permanent link") Use `marimo.hstack` and `marimo.vstack`. See the layout tutorial for details: ### How do I show cell code in the app view?[¶](#how-do-i-show-cell-code-in-the-app-view "Permanent link") Use [`mo.show_code`](https://docs.marimo.io/api/outputs/#marimo.show_code " marimo.show_code"). ### How do I create an output with a dynamic number of UI elements?[¶](#how-do-i-create-an-output-with-a-dynamic-number-of-ui-elements "Permanent link") Use [`mo.ui.array`](https://docs.marimo.io/api/inputs/array/#marimo.ui.array " marimo.ui.array"), [`mo.ui.dictionary`](https://docs.marimo.io/api/inputs/dictionary/#marimo.ui.dictionary " marimo.ui.dictionary"), or [`mo.ui.batch`](https://docs.marimo.io/api/inputs/batch/#marimo.ui.batch " marimo.ui.batch") to create a UI element that wraps a dynamic number of other UI elements. If you need custom formatting, use [`mo.ui.batch`](https://docs.marimo.io/api/inputs/batch/#marimo.ui.batch " marimo.ui.batch"), otherwise use [`mo.ui.array`](https://docs.marimo.io/api/inputs/array/#marimo.ui.array " marimo.ui.array") or [`mo.ui.dictionary`](https://docs.marimo.io/api/inputs/dictionary/#marimo.ui.dictionary " marimo.ui.dictionary"). For usage examples, see the [recipes for grouping UI elements together](https://docs.marimo.io/recipes/#grouping-ui-elements-together). ### How do I restart a notebook?[¶](#how-do-i-restart-a-notebook "Permanent link") To clear all program memory and restart the notebook from scratch, open the notebook menu in the top right and click "Restart kernel". ### How do I reload modules?[¶](#how-do-i-reload-modules "Permanent link") Enable automatic reloading of modules via the runtime settings in your marimo installation's user configuration. (Click the "gear" icon in the top right of a marimo notebook). When enabled, marimo will automatically hot-reload modified modules before executing a cell. ### Why aren't my `on_change`/`on_click` handlers being called?[¶](#why-arent-my-on_changeon_click-handlers-being-called "Permanent link") A UI Element's `on_change` (or for buttons, `on_click`) handlers are only called if the element is bound to a global variable. For example, this won't work `[](#__codelineno-6-1)mo.vstack([mo.ui.button(on_change=lambda _: print("I was called")) for _ in range(10)])` In such cases (when you want to output a dynamic number of UI elements), you need to use [`mo.ui.array`](https://docs.marimo.io/api/inputs/array/#marimo.ui.array " marimo.ui.array"), [`mo.ui.dictionary`](https://docs.marimo.io/api/inputs/dictionary/#marimo.ui.dictionary " marimo.ui.dictionary"), or [`mo.ui.batch`](https://docs.marimo.io/api/inputs/batch/#marimo.ui.batch " marimo.ui.batch"). See the [recipes for grouping UI elements together](https://docs.marimo.io/recipes/#grouping-ui-elements-together) for example code. ### Why are my `on_change` handlers in an array all referencing the last element?[¶](#why-are-my-on_change-handlers-in-an-array-all-referencing-the-last-element "Permanent link") **Don't do this**: In the below snippet, every `on_change` will print `9`!. `[](#__codelineno-7-1)array = mo.ui.array( [](#__codelineno-7-2) [mo.ui.button(on_change=lambda value: print(i)) for i in range(10) [](#__codelineno-7-3)])` **Instead, do this**: Explicitly bind `i` to the current loop value: `[](#__codelineno-8-1)array = mo.ui.array( [](#__codelineno-8-2) [mo.ui.button(on_change=lambda value, i=i: print(i)) for i in range(10)] [](#__codelineno-8-3)) [](#__codelineno-8-4)array` This is necessary because [in Python, closures are late-binding](https://docs.python-guide.org/writing/gotchas/#late-binding-closures). ### Why aren't my SQL brackets working?[¶](#why-arent-my-sql-brackets-working "Permanent link") Our "SQL" cells are really just Python under the hood to keep notebooks as pure Python scripts. By default, we use `f-strings` for SQL strings, which allows for parameterized SQL like `SELECT * from table where value < {min}`. To escape real `{` / `}` that you don't want parameterized, use double `\{\{...\}\}`: `[](#__codelineno-9-1)SELECT unnest([\{\{'a': 42, 'b': 84\}\}, \{\{'a': 100, 'b': NULL\}\}]);` ### How does marimo treat type annotations?[¶](#how-does-marimo-treat-type-annotations "Permanent link") Type annotations are registered as references of a cell, unless they are explicitly written as strings. This helps ensure correctness of code that depends on type annotations at runtime (_e.g._, Pydantic), while still providing a way to omit annotations from affecting dataflow graph. For example, in `A` is treated as a reference, used in determining the dataflow graph, but in `A` isn't made a reference. For Python 3.12+, marimo additionally implements annotation scoping. ### How do I use dotenv?[¶](#how-do-i-use-dotenv "Permanent link") The package `dotenv`'s `loadenv()` function does not work out-of-the box in marimo. Instead, use `dotenv.load_dotenv(dotenv.find_dotenv(usecwd=True))`. ### What packages can I use?[¶](#what-packages-can-i-use "Permanent link") You can use any Python package. marimo cells run arbitrary Python code. ### How do I use marimo on a remote server?[¶](#how-do-i-use-marimo-on-a-remote-server "Permanent link") Use SSH port-forwarding to run marimo on a remote server and connect to it from a browser on your local machine. Make sure to pass the `--headless` flag when starting marimo on remote: You may also want to set a custom host and port: `[](#__codelineno-13-1)marimo edit --headless --host 0.0.0.0 --port 8080` ### How do I make marimo accessible on all network interfaces?[¶](#how-do-i-make-marimo-accessible-on-all-network-interfaces "Permanent link") Use `--host 0.0.0.0` with `marimo edit`, `marimo run`, or `marimo tutorial`: `[](#__codelineno-14-1)marimo edit --host 0.0.0.0` ### How do I use marimo behind JupyterHub?[¶](#how-do-i-use-marimo-behind-jupyterhub "Permanent link") JupyterHub can be configured to launch marimo using the [`jupyter-marimo-proxy` package](https://github.com/jyio/jupyter-marimo-proxy). ### How do I use marimo with JupyterBook?[¶](#how-do-i-use-marimo-with-jupyterbook "Permanent link") [JupyterBook](https://jupyterbook.org/en/stable/intro.html) makes it easy to create static websites with markdown and Jupyter notebooks. To include a marimo notebook in a JupyterBook, you can either export your notebook to an `ipynb` file, or export to `HTML`: 1. export to ipynb: `marimo export ipynb my_notebook.py -o my_notebook.ipynb --include-outputs` 2. export to HTML: `marimo export html my_notebook.py -o my_notebook.html` ### How do I deploy apps?[¶](#how-do-i-deploy-apps "Permanent link") Use the marimo CLI's `run` command to serve a notebook as an app: If you are running marimo inside a Docker container, you may want to run under a different host and port: `[](#__codelineno-16-1)marimo run notebook.py --host 0.0.0.0 --port 8080` ### Is marimo free?[¶](#is-marimo-free "Permanent link") Yes! Community - marimo https://docs.marimo.io/community/ We're building a community. Come hang out with us! * 🌟 [Star us on GitHub](https://github.com/marimo-team/marimo) * 💬 [Chat with us on Discord](https://marimo.io/discord?ref=readme) * 📧 [Subscribe to our Newsletter](https://marimo.io/newsletter) * ☁️ [Join our Cloud Waitlist](https://marimo.io/cloud) * ✏️ [Start a GitHub Discussion](https://github.com/marimo-team/marimo/discussions) * 🦋 [Follow us on Bluesky](https://bsky.app/profile/marimo.io) * 🐦 [Follow us on Twitter](https://twitter.com/marimo_io) * 🎥 [Subscribe on YouTube](https://www.youtube.com/@marimo-team) * 🕴️ [Follow us on LinkedIn](https://www.linkedin.com/company/marimo-io) Shields[¶](#shields "Permanent link") ------------------------------------- You can use our shield for opening a marimo application: * * * **Markdown** `[](#__codelineno-0-1)[![marimo](https://marimo.io/shield.svg)](https://marimo.app/l/c7h6pz)` **HTML** `[](#__codelineno-1-1) [](#__codelineno-1-2) [](#__codelineno-1-3)` Integrations - marimo https://docs.marimo.io/integrations/ It is easy to integrate your preferred data sources or data warehouses with marimo. Since marimo is strictly Python, you can utilize any Python library to access your data. In this section, we provide some examples of how to integrate with popular data sources. | Integration | Description | | --- | --- | | [MotherDuck](https://docs.marimo.io/integrations/motherduck/) | Integrating with MotherDuck | | [Google Cloud Storage](https://docs.marimo.io/integrations/google_cloud_storage/) | Integrating with Google Cloud Storage | | [Google Cloud BigQuery](https://docs.marimo.io/integrations/google_cloud_bigquery/) | Integrating with Google Cloud BigQuery | | [Google Sheets](https://docs.marimo.io/integrations/google_sheets/) | Integrating with Google Sheets | Installation - marimo https://docs.marimo.io/getting_started/installation/ Before installing marimo, we recommend creating and activating a Python [virtual environment](https://docs.python.org/3/tutorial/venv.html#creating-virtual-environments). Setting up a virtual environment Python uses virtual environments to minimize conflicts among packages. Here's a quickstart for `pip` users. If you use `conda`, please use a [`conda` environment](https://conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html#creating-an-environment-with-commands) instead. Run the following in the terminal: * create an environment with `python -m venv marimo-env` * activate the environment: * macOS/Unix: `source marimo-env/bin/activate` * Windows: `marimo-env\Scripts\activate` _Make sure the environment is activated before installing marimo and when using marimo._ Install other packages you may need, such as numpy, pandas, matplotlib, and altair, in this environment. When you're done, deactivate the environment with `deactivate` in the terminal. Learn more from the [official Python tutorial](https://docs.python.org/3/tutorial/venv.html#creating-virtual-environments). Using uv? [uv](https://github.com/astral-sh/uv) is a next-generation Python package installer and manager that is 10-100x faster than pip, and also makes it easy to install Python and manage projects. With `uv`, creating a virtual environment is as easy as `uv venv`. Install with minimal dependencies[¶](#install-with-minimal-dependencies "Permanent link") ----------------------------------------------------------------------------------------- To install marimo, run the following in a terminal: install with pipinstall with uvinstall with conda To check if the install worked, run To check if the install worked, run `[](#__codelineno-3-1)uv run marimo tutorial intro` `[](#__codelineno-4-1)conda install -c conda-forge marimo` To check if the install worked, run A tutorial notebook should open in your browser. Install with recommended dependencies[¶](#install-with-recommended-dependencies "Permanent link") ------------------------------------------------------------------------------------------------- marimo is lightweight, with few dependencies, to maximize compatibility with your own environments. To unlock additional features in the marimo editor, including SQL cells, AI completion, server-side plotting of dataframe columns, and more, we suggest installing `marimo[recommended]`: install with pipinstall with uvinstall with conda `[](#__codelineno-6-1)pip install "marimo[recommended]"` `[](#__codelineno-7-1)uv add "marimo[recommended]"` `[](#__codelineno-8-1)conda install -c conda-forge marimo "duckdb>=1.0.0" "altair>=5.4.0" pyarrow "polars>=1.9.0" "sqlglot>=23.4" "openai>=1.55.3" "ruff" "nbformat>=5.7.0" "vegafusion>=2.0.0" "vl-convert-python>=1.0.0"` Installing marimo in this way installs the following additional dependencies and unlocks the following features: | Dependency | Feature | | --- | --- | | duckdb>=1.0.0 | SQL cells | | altair>=5.4.0 | Plotting in datasource viewer | | polars\[pyarrow\]>=1.9.0 | SQL output back in Python | | sqlglot>=23.4 | SQL cells parsing | | openai>=1.55.3 | AI features | | ruff | Formatting | | nbformat>=5.7.0 | Export as IPYNB | | vegafusion>=2.0.0 | Performant charting | | vl-convert-python>=1.0.0 | Required by vegafusion | Quickstart - marimo https://docs.marimo.io/getting_started/quickstart/ Installing marimo gets you the `marimo` command-line interface (CLI), the entry point to all things marimo. Run tutorials[¶](#run-tutorials "Permanent link") ------------------------------------------------- `marimo tutorial intro` opens the intro tutorial. List all tutorials with Edit notebooks[¶](#edit-notebooks "Permanent link") --------------------------------------------------- Create and edit notebooks with `marimo edit`. * launch the notebook server to create new notebooks, and start or stop existing ones: * create or edit a single notebook with `[](#__codelineno-2-1)marimo edit your_notebook.py` (If `your_notebook.py` doesn't exist, marimo will create a blank notebook named `your_notebook.py`.) Deploy as apps[¶](#deploy-as-apps "Permanent link") --------------------------------------------------- Use `marimo run` to [serve your notebook as an app](https://docs.marimo.io/guides/apps/), with Python code hidden and uneditable. `[](#__codelineno-3-1)marimo run your_notebook.py` Run as scripts[¶](#run-as-scripts "Permanent link") --------------------------------------------------- Run your notebook as a script with You can also [pass CLI args](https://docs.marimo.io/guides/scripts/) to your notebook. Convert from Jupyter to marimo[¶](#convert-from-jupyter-to-marimo "Permanent link") ----------------------------------------------------------------------------------- Automatically convert Jupyter notebooks to marimo notebooks with `marimo convert`: `[](#__codelineno-5-1)marimo convert your_notebook.ipynb -o your_notebook.py` Then open the notebook with `marimo edit your_notebook.py` Disable autorun on startup marimo automatically runs notebooks when they are opened. If this is a problem for you (not all Jupyter notebooks are designed to be run on startup), you can disable autorun on startup via [user configuration](https://docs.marimo.io/guides/configuration/runtime_configuration/). 1. Type `marimo config show` to get the location of your config file. 2. If no config file exists, create it at `$XDG_CONFIG_HOME/marimo/marimo.toml`. 3. Update your config to include the following: marimo.toml `[](#__codelineno-6-1)[runtime] [](#__codelineno-6-2)auto_instantiate = false` Export marimo notebooks to other file formats[¶](#export-marimo-notebooks-to-other-file-formats "Permanent link") ----------------------------------------------------------------------------------------------------------------- Use to [export marimo notebooks](https://docs.marimo.io/guides/exporting/) to other file formats, including HTML, IPYNB, and markdown. Install optional dependencies for more features[¶](#install-optional-dependencies-for-more-features "Permanent link") --------------------------------------------------------------------------------------------------------------------- Some features require additional dependencies, which are not installed by default. This includes: * [SQL cells](https://docs.marimo.io/guides/working_with_data/sql/) * Charts in the datasource viewer * [AI features](https://docs.marimo.io/guides/editor_features/ai_completion/) * Format on save To install the optional dependencies, run: install with pipinstall with uvinstall with conda `[](#__codelineno-8-1)pip install "marimo[recommended]"` `[](#__codelineno-9-1)uv add "marimo[recommended]"` `[](#__codelineno-10-1)conda install -c conda-forge marimo duckdb altair polars openai ruff` This will install: `duckdb`, `altair`, `polars`, `openai`, and `ruff`. Enable GitHub Copilot and AI Assistant[¶](#enable-github-copilot-and-ai-assistant "Permanent link") --------------------------------------------------------------------------------------------------- The marimo editor natively supports [GitHub Copilot](https://copilot.github.com/), an AI pair programmer, similar to VS Code. _Get started with Copilot_: 1. Install [Node.js](https://nodejs.org/en/download). 2. Enable Copilot via the settings menu in the marimo editor. _Note_: Copilot is not yet available in our conda distribution; please install marimo from `PyPI` if you need Copilot. marimo also comes with support for [other copilots](https://docs.marimo.io/guides/editor_features/ai_completion/#custom-copilots), and a built-in [AI assistant](https://docs.marimo.io/guides/editor_features/ai_completion/) that helps you write code. Coming from VS Code?[¶](#coming-from-vs-code "Permanent link") -------------------------------------------------------------- The best way to use marimo is through the CLI. However, if you prefer VS Code over terminal, try our [VS Code extension](https://marketplace.visualstudio.com/items?itemName=marimo-team.vscode-marimo). Use this extension to edit and run notebooks directly from VS Code, and to list all marimo notebooks in your current directory. Key Concepts - marimo https://docs.marimo.io/getting_started/key_concepts/ This page covers marimo's key concepts: * marimo lets you rapidly experiment with data using Python, SQL, and interactive elements in a reproducible **notebook environment**. * Unlike Jupyter notebooks, marimo notebooks are reusable software artifacts. marimo notebooks can be shared as as **interactive web apps** and executed as **Python scripts**. Editing notebooks[¶](#editing-notebooks "Permanent link") --------------------------------------------------------- marimo notebooks are **reactive**: they automatically react to your code changes and UI interactions and keep your notebook up-to-date, not unlike a spreadsheet. This makes your notebooks reproducible, [eliminating hidden state](https://docs.marimo.io/faq/#faq-problems); it's also what enables marimo notebooks to double as apps and Python scripts. Working with expensive notebooks If you don't want cells to run automatically, the [runtime can be configured](https://docs.marimo.io/guides/configuration/runtime_configuration/) to be lazy, only running cells when you ask for them to be run and marking affected cells as stale. **See our guide on working with [expensive notebooks](https://docs.marimo.io/guides/expensive_notebooks/) for more tips.** **Create your first notebook.** After [installing marimo](https://docs.marimo.io/getting_started/installation/), create your first notebook with `[](#__codelineno-0-1)marimo edit my_notebook.py` at the command-line. **The marimo library**. We recommend starting each marimo notebook with a cell containing a single line of code, The marimo library lets you use interactive UI elements, layout elements, dynamic markdown, and more in your marimo notebooks. ### How marimo executes cells[¶](#how-marimo-executes-cells "Permanent link") A marimo notebook is made of small blocks of Python code called **cells**. _When you run a cell, marimo automatically runs all cells that read any global variables defined by that cell._ This is reactive execution. **Execution order.** The order of cells on the page has no bearing on the order cells are executed in: execution order is determined by the variables cells define and the variables they read. You have full freedom over how to organize your code and tell your stories: move helper functions and other "appendices" to the bottom of your notebook, or put cells with important outputs at the top. **No hidden state.** marimo notebooks have no hidden state because the program state is automatically synchronized with your code changes and UI interactions. And if you delete a cell, marimo automatically deletes that cell's variables, preventing painful bugs that arise in traditional notebooks. **No magical syntax.** There's no magical syntax or API required to opt-in to reactivity: cells are Python and _only Python_. Behind-the-scenes, marimo statically analyzes each cell's code just once, creating a directed acyclic graph based on the global names each cell defines and reads. This is how data flows in a marimo notebook. Minimize variable mutation. marimo's understanding of your code is based on variable definitions and references; marimo does not track mutations to objects at runtime. For this reason, if you need to mutate a variable (such as adding a new column to a dataframe), you should perform the mutation in the same cell as the one that defines it. Learn more in our [reactivity guide](https://docs.marimo.io/guides/reactivity/#reactivity-mutations). For more on reactive execution, open the dataflow tutorial or read the [reactivity guide](https://docs.marimo.io/guides/reactivity/). ### Visualizing outputs[¶](#visualizing-outputs "Permanent link") marimo visualizes the last expression of each cell as its **output**. Outputs can be any Python value, including markdown and interactive elements created with the marimo library, (_e.g._, [`mo.md`](https://docs.marimo.io/api/markdown/#marimo.md " marimo.md"), [`mo.ui.slider`](https://docs.marimo.io/api/inputs/slider/#marimo.ui.slider " marimo.ui.slider")). You can even interpolate Python values into markdown (using `mo.md(f"...")`) and other marimo elements to build rich composite outputs: > Thanks to reactive execution, running a cell refreshes all the relevant outputs in your notebook. The marimo library also comes with elements for laying out outputs, including [`mo.hstack`](https://docs.marimo.io/api/layouts/stacks/#marimo.hstack " marimo.hstack"), [`mo.vstack`](https://docs.marimo.io/api/layouts/stacks/#marimo.vstack " marimo.vstack"), [`mo.accordion`](https://docs.marimo.io/api/layouts/accordion/#marimo.accordion " marimo.accordion"), [`mo.ui.tabs`](https://docs.marimo.io/api/inputs/tabs/#marimo.ui.tabs " marimo.ui.tabs"), [`mo.sidebar`](https://docs.marimo.io/api/layouts/sidebar/#marimo.sidebar " marimo.sidebar"), [`mo.nav_menu`](https://docs.marimo.io/api/inputs/nav_menu/#marimo.nav_menu " marimo.nav_menu"), [`mo.ui.table`](https://docs.marimo.io/api/inputs/table/#marimo.ui.table " marimo.ui.table"), and [many more](https://docs.marimo.io/api/layouts/). For more on outputs, try these tutorials: `[](#__codelineno-3-1)marimo tutorial markdown [](#__codelineno-3-2)marimo tutorial plots [](#__codelineno-3-3)marimo tutorial layout` or read the [visualizing outputs guide](https://docs.marimo.io/guides/outputs/). ### Creating interactive elements[¶](#creating-interactive-elements "Permanent link") The marimo library comes with many interactive stateful elements in [`marimo.ui`](https://docs.marimo.io/api/inputs/), including simple ones like sliders, dropdowns, text fields, and file upload areas, as well as composite ones like forms, arrays, and dictionaries that can wrap other UI elements. **Using UI elements.** To use a UI element, create it with `mo.ui` and **assign it to a global variable.** When you interact with a UI element in your browser (_e.g._, sliding a slider), _marimo sends the new value back to Python and reactively runs all cells that use the element_, which you can access via its `value` attribute. > **This combination of interactivity and reactivity is very powerful**: use it to make your data tangible during exploration and to build all kinds of tools and apps. _marimo can only synchronize UI elements that are assigned to global variables._ Use composite elements like [`mo.ui.array`](https://docs.marimo.io/api/inputs/array/#marimo.ui.array " marimo.ui.array") and [`mo.ui.dictionary`](https://docs.marimo.io/api/inputs/dictionary/#marimo.ui.dictionary " marimo.ui.dictionary") if the set of UI elements is not known until runtime. Using buttons to execute cells Use [`mo.ui.run_button`](https://docs.marimo.io/api/inputs/run_button/#marimo.ui.run_button " marimo.ui.run_button") to create a button that triggers computation when clicked; see our recipes for [an example](https://docs.marimo.io/recipes/#create-a-button-that-triggers-computation-when-clicked). For more on interactive elements, run the UI tutorial or read the [interactivity guide](https://docs.marimo.io/guides/interactivity/). ### Querying dataframes and databases with SQL[¶](#querying-dataframes-and-databases-with-sql "Permanent link") marimo has built-in support for SQL: you can query Python dataframes, databases, CSVs, Google Sheets, or anything else. After executing your query, marimo returns the result to you as a dataframe, making it seamless to go back and forth between SQL and Python. Query a dataframe using SQL! To create a SQL cell, click on the SQL button that appears at the bottom of the cell array, or right click the create cell button next to a cell. Today, SQL in marimo is executed using [duckdb](https://duckdb.org/docs/). To learn more, run the SQL tutorial or read the [SQL guide](https://docs.marimo.io/guides/working_with_data/sql/). Running notebooks as applications[¶](#running-notebooks-as-applications "Permanent link") ----------------------------------------------------------------------------------------- You can use marimo as a notebook, similar to how you might use Jupyter. But you can also do more: because marimo notebooks are reactive and can include interactive elements, hiding notebook code gives you a simple web app! You can run your notebook as a read-only web app from the command-line: `[](#__codelineno-6-1)marimo run my_notebook.py` The default renderer just hides the notebook code and concatenates outputs vertically. But marimo also supports [other layouts](https://docs.marimo.io/guides/apps/), such as slides and grid. Running notebooks as scripts[¶](#running-notebooks-as-scripts "Permanent link") ------------------------------------------------------------------------------- Because marimo notebooks are stored as pure Python files, each notebook can be executed as a script from the command-line: You can also [pass command-line arguments](https://docs.marimo.io/guides/scripts/) to scripts. Readings & Videos - marimo https://docs.marimo.io/reading/ Resources[¶](#resources "Permanent link") ----------------------------------------- marimo is a **reinvention** of the Python notebook. As a reinvention, marimo may push to you **rethink** what a notebook **is**. The following readings and videos might help you do just that. To dive deep into what we're building and why, check out these readings: * [Our HackerNews launch, the second most upvoted Python ShowHN of all time](https://news.ycombinator.com/item?id=38971966) * [Our r/machinelearning launch](https://news.ycombinator.com/item?id=38971966) * [Lessons Learned Reinventing the Python Notebook](https://marimo.io/blog/lessons-learned) * [Why Stanford Scientists Needed a New Notebook](https://marimo.io/blog/slac-marimo) * [Reinventing Python Notebooks as Reusable Python Programs](https://marimo.io/blog/python-not-json) * [Nature: a Notebook for Reproducible Code](https://www.nature.com/articles/d41586-025-01241-6) See [our blog](https://marimo.io/blog) for more. YouTube[¶](#youtube "Permanent link") ------------------------------------- Our [YouTube channel](https://www.youtube.com/@marimo-team) shows you the ins and outs of using marimo for many applications, including AI, ML, data engineering, and more. You can also get started with the [marimo concepts](https://www.youtube.com/watch?v=3N6lInzq5MI&list=PLNJXGo8e1XT9jP7gPbRdm1XwloZVFvLEq) playlist, which tours many of our features. Running cells - marimo https://docs.marimo.io/guides/reactivity/ marimo _reacts_ to your code changes: run a cell, and all other cells that refer to the variables it defines are automatically run with the latest data. This keeps your code and outputs consistent, and eliminates bugs before they happen. Why run cells reactively? marimo's "reactive" execution model makes your notebooks more reproducible by eliminating hidden state and providing a deterministic execution order. It also powers marimo's support for [interactive elements](https://docs.marimo.io/guides/interactivity/), for running as apps, and executing as scripts. How marimo runs cells is one of the biggest differences between marimo and traditional notebooks like Jupyter. Learn more at our [FAQ](https://docs.marimo.io/faq/#faq-jupyter). Working with expensive notebooks marimo provides tools for working with expensive notebooks, in which cells might take a long time to run or have side-effects. * The [runtime can be configured](https://docs.marimo.io/guides/configuration/runtime_configuration/) to be **lazy** instead of automatic, marking cells as stale instead of running them. * Use [`mo.stop`](https://docs.marimo.io/api/control_flow/#marimo.stop " marimo.stop") to conditionally stop execution at runtime. See [the expensive notebooks guide](https://docs.marimo.io/guides/expensive_notebooks/) for more tips. How marimo runs cells[¶](#how-marimo-runs-cells "Permanent link") ----------------------------------------------------------------- marimo statically analyzes each cell (i.e., without running it) to determine its * references, the global variables it reads but doesn't define; * definitions, the global variables it defines. It then forms a directed acyclic graph (DAG) on cells, with an edge from one cell to another if the latter references any of the definitions of the former. When a cell is run, its descendants are marked for execution. Runtime Rule When a cell is run, marimo automatically runs all other cells that **reference** any of the global variables it **defines**. marimo [does not track mutations](#variable-mutations-are-not-tracked) to variables, nor assignments to attributes. That means that if you assign an attribute like `foo.bar = 10`, other cells referencing `foo.bar` will _not_ be run. ### Execution order[¶](#execution-order "Permanent link") The order cells are executed in is determined by the relationships between cells and their variables, not by the order of cells on the page (similar to a spreadsheet). This lets you organize your code in whatever way makes the most sense to you. For example, you can put helper functions at the bottom of your notebook. ### Deleting a cell deletes its variables[¶](#deleting-a-cell-deletes-its-variables "Permanent link") In marimo, _deleting a cell deletes its global variables from program memory_. Cells that previously referenced these variables are automatically re-run and invalidated (or marked as stale, depending on your [runtime configuration](https://docs.marimo.io/guides/configuration/runtime_configuration/)). In this way, marimo eliminates a common cause of bugs in traditional notebooks like Jupyter. ### Variable mutations are not tracked[¶](#variable-mutations-are-not-tracked "Permanent link") marimo does not track mutations to objects, _e.g._, mutations like `my_list.append(42)` or `my_object.value = 42` don't trigger reactive re-runs of other cells. **Avoid defining a variable in one cell and mutating it in another**. Why not track mutations? Tracking mutations reliably is impossible in Python. Reacting to mutations could result in surprising re-runs of notebook cells. If you need to mutate a variable (such as adding a new column to a dataframe), you should perform the mutation in the same cell as the one that defines it, or try creating a new variable instead. Create new variables, don't mutate existing ones Mutate variables in the cells that define them Do this ...... not this `[](#__codelineno-4-1)df = pd.DataFrame({"my_column": [1, 2]}) [](#__codelineno-4-2)df["another_column"] = [3, 4]` `[](#__codelineno-5-1)df = pd.DataFrame({"my_column": [1, 2]})` `[](#__codelineno-6-1)df["another_column"] = [3, 4]` Global variable names must be unique[¶](#global-variable-names-must-be-unique "Permanent link") ----------------------------------------------------------------------------------------------- **marimo requires that every global variable be defined by only one cell.** This lets marimo keep code and outputs consistent. Global variables A variable can refer to any Python object. Functions, classes, and imported names are all variables. This rule encourages you to keep the number of global variables in your program small, which is generally considered good practice. ### Creating temporary variables[¶](#creating-temporary-variables "Permanent link") marimo provides two ways to define temporary variables, which can help keep the number of global variables in your notebook small. #### Creating local variables[¶](#creating-local-variables "Permanent link") Variables prefixed with an underscore (_e.g._, `_x`) are "local" to a cell: they can't be read by other cells. Multiple cells can reuse the same local variables names. #### Encapsulating code in functions[¶](#encapsulating-code-in-functions "Permanent link") If you want most or all the variables in a cell to be temporary, prefixing each variable with an underscore to make it local may feel inconvenient. In these situations we recommend encapsulating the temporary variables in a function. For example, if you find yourself copy-pasting the same plotting code across multiple cells and only tweaking a few parameters, try the following pattern: `[](#__codelineno-7-1)def _(): [](#__codelineno-7-2) import matplotlib.pyplot as plt [](#__codelineno-7-3) fig, ax = plt.subplots() [](#__codelineno-7-4) ax.plot([1, 2]) [](#__codelineno-7-5) return ax [](#__codelineno-7-6)[](#__codelineno-7-7)_()` Here, the variables `plt`, `fig`, and `ax` aren't added to the globals. Configuring how marimo runs cells[¶](#configuring-how-marimo-runs-cells "Permanent link") ----------------------------------------------------------------------------------------- Through the notebook settings menu, you can configure how and when marimo runs cells. In particular, you can disable autorun on startup, disable autorun on cell execution, and enable a module autoreloader. Read our [runtime configuration guide](https://docs.marimo.io/guides/configuration/runtime_configuration/) to learn more. Disabling cells[¶](#disabling-cells "Permanent link") ----------------------------------------------------- Sometimes, you may want to edit one part of a notebook without triggering automatic execution of its dependent cells. For example, the dependent cells may take a long time to execute, and you only want to iterate on the first part of a multi-cell computation. For cases like this, marimo lets you **disable** cells: when a cell is disabled, it and its dependents are blocked from running. Disabling a cell blocks it from running. When you re-enable a cell, if any of the cell's ancestors ran while it was disabled, marimo will automatically run it. Enable a cell through the context menu. Stale cells run automatically. Interactive elements - marimo https://docs.marimo.io/guides/interactivity/ One of marimo's most powerful features is its first-class support for interactive user interface (UI) elements, or "widgets", created using [`marimo.ui`](https://docs.marimo.io/api/inputs/). **Interacting with a UI element bound to a global variable automatically runs all cells that reference it.** How interactions run cells[¶](#how-interactions-run-cells "Permanent link") --------------------------------------------------------------------------- Every UI element you make using [`marimo.ui`](https://docs.marimo.io/api/inputs/) has a value, accessible via its `value` attribute. When you interact with a UI element bound to a global variable, its value is sent back to Python. A single rule determines what happens next: Interaction rule When a UI element assigned to a global variable is interacted with, marimo automatically runs all cells that reference the variable (but don't define it). In the clip at the top of this page, interacting with the slider in the second cell re-runs the third cell (which outputs markdown) because it references the slider variable `x`. It doesn't re-run the second cell, because that cell defines `x`. **For interactions on a UI element to have any effect, the element must be assigned to a global variable.** Displaying UI elements[¶](#displaying-ui-elements "Permanent link") ------------------------------------------------------------------- Display UI elements in the output area above a cell by including them in the last expression, just like any other object. You can also embed elements in [markdown](https://docs.marimo.io/api/markdown/#marimo.md " marimo.md") using Python f-strings, like so: `[](#__codelineno-0-1)slider = mo.ui.slider(1, 10) [](#__codelineno-0-2)mo.md(f"Choose a value: {slider}")` Composite elements[¶](#composite-elements "Permanent link") ----------------------------------------------------------- Composite elements are advanced elements let you build UI elements out of other UI elements. The following composite elements are available: * [`mo.ui.array`](https://docs.marimo.io/api/inputs/array/#marimo.ui.array " marimo.ui.array") * [`mo.ui.dictionary`](https://docs.marimo.io/api/inputs/dictionary/#marimo.ui.dictionary " marimo.ui.dictionary") * [`mo.ui.batch`](https://docs.marimo.io/api/inputs/batch/#marimo.ui.batch " marimo.ui.batch") * [`mo.ui.form`](https://docs.marimo.io/api/inputs/form/#marimo.ui.form " marimo.ui.form") **Arrays and dictionaries.** Use [`mo.ui.array`](https://docs.marimo.io/api/inputs/array/#marimo.ui.array " marimo.ui.array") and [`mo.ui.dictionary`](https://docs.marimo.io/api/inputs/dictionary/#marimo.ui.dictionary " marimo.ui.dictionary") to logically group together related elements. These elements are especially useful when a set of UI elements is only known at runtime (so you can't assign each to a global variable individually, but can assign them to an array or dictionary). You can access the elements contained in an array or dictionary using Pythonic syntax, and embed these elements in other outputs. See their docstrings for code examples. **Batch and form.** Use these powerful elements to group together multiple UI elements into a single element with custom formatting, and gate the sending of an element's value on form submission. Use a form to gate value updates on submission Use an array to group together elements or create a collection of elements that is determined at runtime Building custom UI elements using our plugin API[¶](#building-custom-ui-elements-using-our-plugin-api "Permanent link") ----------------------------------------------------------------------------------------------------------------------- You can build your own reactive and interactive UI elements using [anywidget](https://github.com/manzt/anywidget). See [our docs on building custom UI elements](https://docs.marimo.io/guides/integrating_with_marimo/custom_ui_plugins/) to learn more. Visualize outputs - marimo https://docs.marimo.io/guides/outputs/ Visualizing outputs[¶](#visualizing-outputs "Permanent link") ------------------------------------------------------------- The last expression of a cell is its visual output, rendered above the cell. Outputs are included in the "app" or read-only view of the notebook. marimo comes out of the box a number of elements to help you make rich outputs, documented in the [API reference](https://docs.marimo.io/api/). Markdown[¶](#markdown "Permanent link") --------------------------------------- Markdown is written with the marimo library function [`mo.md`](https://docs.marimo.io/api/markdown/#marimo.md " marimo.md"). Writing markdown programmatically lets you make dynamic markdown: interpolate Python values into markdown strings, conditionally render your markdown, and embed markdown in other objects. Here's a simple hello world example: `[](#__codelineno-1-1)name = mo.ui.text(placeholder="Your name here") [](#__codelineno-1-2)mo.md( [](#__codelineno-1-3) f""" [](#__codelineno-1-4) Hi! What's your name? [](#__codelineno-1-5) [](#__codelineno-1-6) {name} [](#__codelineno-1-7) """ [](#__codelineno-1-8))` `[](#__codelineno-2-1)mo.md( [](#__codelineno-2-2) f""" [](#__codelineno-2-3) Hello, {name.value}! [](#__codelineno-2-4) """ [](#__codelineno-2-5))` Notice that marimo knows how to render marimo objects in markdown: you can just embed them in [`mo.md()`](https://docs.marimo.io/api/markdown/#marimo.md " marimo.md") using an f-string, and marimo will figure out how to display them! For other objects, like matplotlib plots, wrap them in [`mo.as_html()`](https://docs.marimo.io/api/html/#marimo.as_html " marimo.as_html") to tap into marimo's media viewer: `[](#__codelineno-3-1)mo.md( [](#__codelineno-3-2) f""" [](#__codelineno-3-3) Here's a plot! [](#__codelineno-3-4) [](#__codelineno-3-5) {mo.as_html(figure)} [](#__codelineno-3-6) """ [](#__codelineno-3-7))` ### Markdown editor[¶](#markdown-editor "Permanent link") marimo automatically renders cells that only use `mo.md` in a markdown editor that supports common hotkeys. You can switch between the Markdown and Python editors by clicking the button in the top right: marimo is pure Python, even when you're using markdown. **Writing LaTeX.** The markdown editor supports writing LaTeX. You should typically use a raw string for markdown with LaTeX, which you can activate by checking the `r` box in the bottom-right corner of the markdown editor. **Interpolating Python values.** Interpolating Python values requires using an `f`\-string, which you can activate by checking the `f` box in the bottom-right corner of the markdown editor. ### Markdown extensions[¶](#markdown-extensions "Permanent link") #### Details[¶](#details "Permanent link") Create expandable details with additional context: `[](#__codelineno-4-1)/// details | Heads up [](#__codelineno-4-2)[](#__codelineno-4-3)Here's some additional context. [](#__codelineno-4-4)///` Source code for `examples/markdown/details.py` Tip: paste this code into an empty cell, and the marimo editor will create cells for you ``import marimo __generated_with = "0.10.19" app = marimo.App() @app.cell(hide_code=True) def _(mo): mo.md(r"""Create expandable markdown blocks with `details`:""") return @app.cell def _(mo): mo.md( """ /// details | Hello, details! Some additional content. /// """ ) return @app.cell(hide_code=True) def _(mo): mo.md(r"""Style details using the "type" argument:""") return @app.cell def _(mo): mo.md( """ /// details | Info details type: info Some additional content. /// """ ) return @app.cell def _(mo): mo.md( """ /// details | Warning details type: warn This highlights something to watch out for /// """ ) return @app.cell def _(mo): mo.md( """ /// details | Danger details type: danger This indicates a critical warning or dangerous situation /// """ ) return @app.cell def _(mo): mo.md( """ /// details | Success details type: success This indicates a successful outcome or positive note /// """ ) return @app.cell def _(): import marimo as mo return (mo,) if __name__ == "__main__": app.run()`` #### Admonitions[¶](#admonitions "Permanent link") Highlight text using admonitions: `[](#__codelineno-5-1)/// attention | This is important. [](#__codelineno-5-2)[](#__codelineno-5-3)Pay attention to this text! [](#__codelineno-5-4)///` Source code for `examples/markdown/admonitions.py` Tip: paste this code into an empty cell, and the marimo editor will create cells for you `import marimo __generated_with = "0.10.19" app = marimo.App() @app.cell(hide_code=True) def _(mo): mo.md(r"""Use **admonitions** in markdown to bring attention to text. Here are some examples.""") return @app.cell def _(mo): mo.md( """ /// admonition | Heads up. Here's some information. /// """ ) return @app.cell def _(mo): mo.md( """ /// attention | Attention! This is important. /// """ ) return @app.cell def _(): import marimo as mo return (mo,) if __name__ == "__main__": app.run()` #### Emoji[¶](#emoji "Permanent link") Use `:emoji:` syntax to add emojis; for example, `:rocket:` creates 🚀. ### Static files[¶](#static-files "Permanent link") marimo supports serving static files from a `public/` folder located next to your notebook. This is useful for including images or other static assets in your notebook. To use files from the public folder, create a `public` directory next to your notebook and reference files using the `public/` path prefix: `[](#__codelineno-6-1)mo.md( [](#__codelineno-6-2) ''' [](#__codelineno-6-3) [](#__codelineno-6-4) [](#__codelineno-6-5) or [](#__codelineno-6-6) [](#__codelineno-6-7) ![alt text](public/image.png) [](#__codelineno-6-8) ''' [](#__codelineno-6-9))` For security reasons: * Only files within the `public` directory can be accessed * Symlinks are not followed * Path traversal attempts (e.g., `../`) are blocked Layout[¶](#layout "Permanent link") ----------------------------------- The marimo library also comes with elements for laying out outputs, including [`mo.hstack`](https://docs.marimo.io/api/layouts/stacks/#marimo.hstack " marimo.hstack"), [`mo.vstack`](https://docs.marimo.io/api/layouts/stacks/#marimo.vstack " marimo.vstack"), [`mo.accordion`](https://docs.marimo.io/api/layouts/accordion/#marimo.accordion " marimo.accordion"), [`mo.ui.tabs`](https://docs.marimo.io/api/inputs/tabs/#marimo.ui.tabs " marimo.ui.tabs"), [`mo.sidebar`](https://docs.marimo.io/api/layouts/sidebar/#marimo.sidebar " marimo.sidebar"), [`mo.nav_menu`](https://docs.marimo.io/api/inputs/nav_menu/#marimo.nav_menu " marimo.nav_menu"), [`mo.ui.table`](https://docs.marimo.io/api/inputs/table/#marimo.ui.table " marimo.ui.table"), and [many more](https://docs.marimo.io/api/layouts/). Progress bars[¶](#progress-bars "Permanent link") ------------------------------------------------- Use [`mo.status.progress_bar`](https://docs.marimo.io/api/status/#marimo.status.progress_bar " marimo.status.progress_bar") and [`mo.status.spinner`](https://docs.marimo.io/api/status/#marimo.status.spinner " marimo.status.spinner") to create progress indicators: `[](#__codelineno-7-1)# mo.status.progress_bar is similar to TQDM [](#__codelineno-7-2)for i in mo.status.progress_bar(range(10)): [](#__codelineno-7-3) print(i)` marimo comes with functions to display media, including images, audio, video, pdfs, and more. See the [API docs](https://docs.marimo.io/api/media/) for more info. Imperatively adding outputs[¶](#imperatively-adding-outputs "Permanent link") ----------------------------------------------------------------------------- While a cell's output is its last expression, it can at times be helpful to imperatively add to the output area while a cell is running. marimo provides utility functions like [`mo.output.append`](https://docs.marimo.io/api/outputs/#marimo.output.append " marimo.output.append") for accomplishing this; see the [API docs](https://docs.marimo.io/api/outputs/) for more information. Console Outputs[¶](#console-outputs "Permanent link") ----------------------------------------------------- Console outputs, such as print statements, show up below a cell in the console output area; they are not included in the output area or app view by default. To include console outputs in the cell output area, use [`mo.redirect_stdout`](https://docs.marimo.io/api/outputs/#marimo.redirect_stdout " marimo.redirect_stdout") or [`mo.redirect_stderr`](https://docs.marimo.io/api/outputs/#marimo.redirect_stderr " marimo.redirect_stderr"): `[](#__codelineno-8-1)with mo.redirect_stdout(): [](#__codelineno-8-2) print("Hello, world!")` marimo also includes utility functions for [capturing standard out](https://docs.marimo.io/api/outputs/#marimo.capture_stdout " marimo.capture_stdout") and [standard error](https://docs.marimo.io/api/outputs/#marimo.capture_stderr " marimo.capture_stderr") without redirecting them. See the [API docs](https://docs.marimo.io/api/outputs/#console-outputs) for more. Threading[¶](#threading "Permanent link") ----------------------------------------- To create a thread that can reliably communicate outputs to the frontend, use [`mo.Thread`](https://docs.marimo.io/api/control_flow/#marimo.Thread " marimo.Thread"), which has exactly the same API as as `threading.Thread`. ### Cleaning up your thread[¶](#cleaning-up-your-thread "Permanent link") When the cell that spawned a [`mo.Thread`](https://docs.marimo.io/api/control_flow/#marimo.Thread " marimo.Thread") is invalidated (re-run, deleted, interrupted, or otherwise errored), the thread's `should_exit` property will evaluate to `True`, at which point it is your responsibility to clean up your thread. You can retrieve the current [`mo.Thread`](https://docs.marimo.io/api/control_flow/#marimo.Thread " marimo.Thread") with [`mo.current_thread`](https://docs.marimo.io/api/control_flow/#marimo.current_thread " marimo.current_thread"). **Example.** `[](#__codelineno-9-1)def target(): [](#__codelineno-9-2) import marimo as mo [](#__codelineno-9-3) [](#__codelineno-9-4) thread = mo.current_thread() [](#__codelineno-9-5) while not thread.should_exit: [](#__codelineno-9-6) ...` ### Patching threads created by third-party code[¶](#patching-threads-created-by-third-party-code "Permanent link") If you need to forward outputs from threads spawned by third-party code, try patching `threading.Thread`: `[](#__codelineno-10-1)import threading [](#__codelineno-10-2)import marimo as mo [](#__codelineno-10-3)[](#__codelineno-10-4)threading.Thread = mo.Thread` This however may leak threads, since the patched threads won't know to check the `mo.Thread`'s `should_exit` property. Expensive notebooks - marimo https://docs.marimo.io/guides/expensive_notebooks/ Working with expensive notebooks[¶](#working-with-expensive-notebooks "Permanent link") --------------------------------------------------------------------------------------- marimo provides tools to control when cells run. Use these tools to prevent expensive cells, which may call APIs or take a long time to run, from accidentally running. Stop execution with `mo.stop`[¶](#stop-execution-with-mostop "Permanent link") ------------------------------------------------------------------------------ Use [`mo.stop`](https://docs.marimo.io/api/control_flow/#marimo.stop " marimo.stop") to stop a cell from executing if a condition is met: `[](#__codelineno-0-1)# if condition is True, the cell will stop executing after mo.stop() returns [](#__codelineno-0-2)mo.stop(condition) [](#__codelineno-0-3)# this won't be called if condition is True [](#__codelineno-0-4)expensive_function_call()` Use [`mo.stop`](https://docs.marimo.io/api/control_flow/#marimo.stop " marimo.stop") with [`mo.ui.run_button()`](https://docs.marimo.io/api/inputs/run_button/#marimo.ui.run_button " marimo.ui.run_button") to require a button press for expensive cells: Configure how marimo runs cells[¶](#configure-how-marimo-runs-cells "Permanent link") ------------------------------------------------------------------------------------- ### Disabling cell autorun[¶](#disabling-cell-autorun "Permanent link") If you habitually work with very expensive notebooks, you can [disable automatic execution](https://docs.marimo.io/guides/configuration/runtime_configuration/#on-cell-change). When automatic execution is disabled, when you run a cell, marimo marks dependent cells as stale instead of running them automatically. ### Disabling autorun on startup[¶](#disabling-autorun-on-startup "Permanent link") marimo autoruns notebooks on startup, with `marimo edit notebook.py` behaving analogously to `python notebook.py`. This can also be disabled through the [notebook settings](https://docs.marimo.io/guides/configuration/runtime_configuration/#on-startup). ### Disable individual cells[¶](#disable-individual-cells "Permanent link") marimo lets you temporarily disable cells from automatically running. This is helpful when you want to edit one part of a notebook without triggering execution of other parts. See the [reactivity guide](https://docs.marimo.io/guides/reactivity/#disabling-cells) for more info. Automatic snapshotting as HTML or ipynb[¶](#automatic-snapshotting-as-html-or-ipynb "Permanent link") ----------------------------------------------------------------------------------------------------- To keep a record of your cell outputs while working on your notebook, you can configure notebooks to automatically save as HTML or ipynb through the notebook menu (these files are saved in addition to the notebook's `.py` file). Snapshots are saved to a folder called `__marimo__` in the notebook directory. Learn more about exporting notebooks in our [exporting guide](https://docs.marimo.io/guides/exporting/). Caching[¶](#caching "Permanent link") ------------------------------------- marimo provides two caching utilities to help you manage expensive computations: 1. In-memory caching with [`mo.cache`](https://docs.marimo.io/api/caching/#marimo.cache " marimo.cache") 2. Disk caching with [`mo.persistent_cache`](https://docs.marimo.io/api/caching/#marimo.persistent_cache " marimo.persistent_cache") Both utilities can be used as decorators or context managers. ### In-memory caching[¶](#in-memory-caching "Permanent link") Use [`mo.cache`](https://docs.marimo.io/api/caching/#marimo.cache " marimo.cache") to cache the return values of expensive functions, based on their arguments: decoratorcontext manager `[](#__codelineno-2-1)import marimo as mo [](#__codelineno-2-2)[](#__codelineno-2-3)@mo.cache [](#__codelineno-2-4)def compute_predictions(problem_parameters): [](#__codelineno-2-5) # do some expensive computations and return a value [](#__codelineno-2-6) ...` `[](#__codelineno-3-1)import marimo as mo [](#__codelineno-3-2)[](#__codelineno-3-3)with mo.cache("my_cache") as c: [](#__codelineno-3-4) predictions = compute_predictions(problem_parameters):` When `compute_predictions` is called with a value of `problem_parameters` it hasn't seen, it will compute the predictions and store them in an in-memory cache. The next time it is called with the same parameters, instead of recomputing the predictions, it will return the previously computed value from the cache. Comparison to `functools.cache` [`mo.cache`](https://docs.marimo.io/api/caching/#marimo.cache " marimo.cache") is like `functools.cache` but smarter. `functools` will sometimes evict values from the cache when it doesn't need to. In particular, consider the case when a cell defining a `@mo.cache`\-d function re-runs due to an ancestor of it running, or a UI element value changing. `mo.cache` will analyze the dataflow graph to determine whether or not the decorated function has changed, and if it hasn't, it's cache won't be invalidated. In contrast, on re-run a `functools` cache is always invalidated, because `functools` has no knowledge about the structure of marimo's dataflow graph. Conversely, [`mo.cache`](https://docs.marimo.io/api/caching/#marimo.cache " marimo.cache") knows to invalidate the cache if closed over variables change, whereas `functools.cache` doesn't, yielding incorrect cache hits. [`mo.cache`](https://docs.marimo.io/api/caching/#marimo.cache " marimo.cache") is slightly slower than `functools.cache`, but in most applications the overhead is negligible. For performance critical code, where the decorated function will be called in a tight loop, prefer `functools.cache`. ### Disk caching[¶](#disk-caching "Permanent link") Use [`mo.persistent_cache`](https://docs.marimo.io/api/caching/#marimo.persistent_cache " marimo.persistent_cache") to cache variables to disk. The next time your run your notebook, the cached variables will be loaded from disk instead of being recomputed, letting you pick up where you left off. Reserve this for expensive computations that you would like to persist across notebook restarts. Cached outputs are automatically saved to `__marimo__/cache`. **Example.** decoratorcontext manager `[](#__codelineno-4-1)import marimo as mo [](#__codelineno-4-2)[](#__codelineno-4-3)@mo.persistent_cache(name="my_cache") [](#__codelineno-4-4)def compute_predictions(problem_parameters): [](#__codelineno-4-5) # do some expensive computations and return a value [](#__codelineno-4-6) ...` ```[](#__codelineno-5-1)import marimo as mo [](#__codelineno-5-2)[](#__codelineno-5-3)with mo.persistent_cache(name="my_cache"): [](#__codelineno-5-4) # This block of code and its computed variables will be cached to disk [](#__codelineno-5-5) # the first time it's run. The next time it's run, `predictions`` [](#__codelineno-5-6) # will be loaded from disk. [](#__codelineno-5-7) predictions = compute_predictions(problem_parameters) [](#__codelineno-5-8) ...``` Roughly speaking, [`mo.persistent_cache`](https://docs.marimo.io/api/caching/#marimo.persistent_cache " marimo.persistent_cache") registers a cache hit when the cell is not stale, meaning its code hasn't changed and neither have its ancestors. On cache hit the code block won't execute and instead variables will be loaded into memory. Lazy-load expensive UIs[¶](#lazy-load-expensive-uis "Permanent link") --------------------------------------------------------------------- Lazily render UI elements that are expensive to compute using `marimo.lazy`. For example, `[](#__codelineno-6-1)import marimo as mo [](#__codelineno-6-2)[](#__codelineno-6-3)data = db.query("SELECT * FROM data") [](#__codelineno-6-4)mo.lazy(mo.ui.table(data))` In this example, `mo.ui.table(data)` will not be rendered on the frontend until is it in the viewport. For example, an element can be out of the viewport due to scroll, inside a tab that is not selected, or inside an accordion that is not open. However, in this example, data is eagerly computed, while only the rendering of the table is lazy. It is possible to lazily compute the data as well: see the next example. `[](#__codelineno-7-1)import marimo as mo [](#__codelineno-7-2)[](#__codelineno-7-3)def expensive_component(): [](#__codelineno-7-4) import time [](#__codelineno-7-5) time.sleep(1) [](#__codelineno-7-6) data = db.query("SELECT * FROM data") [](#__codelineno-7-7) return mo.ui.table(data) [](#__codelineno-7-8)[](#__codelineno-7-9)accordion = mo.accordion({ [](#__codelineno-7-10) "Charts": mo.lazy(expensive_component) [](#__codelineno-7-11)})` In this example, we pass a function to `mo.lazy` instead of a component. This function will only be called when the user opens the accordion. In this way, `expensive_component` lazily computed and we only query the database when the user needs to see the data. This can be useful when the data is expensive to compute and the user may not need to see it immediately. Understanding errors - marimo https://docs.marimo.io/guides/understanding_errors/ marimo imposes a few constraints on your notebook code: * no multiply defined variables: each variable can be defined in only one cell * no cycles: if one cell declares variable `a` and reads `b`, then another cannot declare `b` and read `a`. * no `import *`: importing all symbols from a library is not allowed Why these constraints? These constraints let marimo work its magic, making your notebooks: * **reproducible**, with a well-defined execution order, no hidden state, and no hidden bugs; * **executable** as a script; * **interactive** with UI elements that work without callbacks; * **shareable as a web app**, with far better performance than streamlit. As a bonus, you'll find that you end up with cleaner, reusable code. When a cell violates any of these constraints, marimo doesn't run it and instead reports an error. In these guides, we explain these errors and provide tips for how to work around them. These errors might be surprising at first, but spend just a bit of time with marimo and adhering to these constraints will become second nature — and you'll get used to writing error-free code by default. | Guide | Description | | --- | --- | | [Multiple definitions](https://docs.marimo.io/guides/understanding_errors/multiple_definitions/) | How to deal with variables defined in multiple cells | | [Import `*`](https://docs.marimo.io/guides/understanding_errors/import_star/) | Why you can't use `import *` | | [Cycles](https://docs.marimo.io/guides/understanding_errors/cycles/) | How to resolve cycle errors | | [setup](https://docs.marimo.io/guides/understanding_errors/setup/) | How to enable top level definitions | Migrate from Jupyter - marimo https://docs.marimo.io/guides/coming_from/jupyter/ Coming from Jupyter[¶](#coming-from-jupyter "Permanent link") ------------------------------------------------------------- If you're coming from Jupyter, here are a few tips to help you adapt to marimo notebooks. How marimo runs cells[¶](#how-marimo-runs-cells "Permanent link") ----------------------------------------------------------------- The biggest difference between marimo and Jupyter is the [execution model](https://docs.marimo.io/guides/reactivity/). A **Jupyter** notebook is a **REPL**: you execute blocks of code one at a time, and Jupyter has no understanding of how different blocks are related to each other. As a result a Jupyter notebook can easily accumulate **"hidden state"** (and hidden bugs) --- you might accidentally execute cells out of order, or you might run (or delete) a cell but forget to re-run cells that depended on its variables. Because of this, Jupyter notebooks suffer from a [reproducibility crisis](https://docs.marimo.io/faq/#faq-problems), with over a third of Jupyter notebooks on GitHub failing to reproduce. Unlike Jupyter, **marimo** notebooks understand how different blocks of code are related to each other, modeling your code as a graph on cells based on variable declarations and references. This eliminates hidden state, and it's also what enables marimo notebooks to be reused as apps and scripts. **By default, if you run a cell in marimo, all other cells that read its variables run automatically.** While this ensures that your code and outputs are in sync, it can take some time getting used to. **Here are some tips and tools to help you adapt to marimo's execution model.** ### Configure marimo's runtime[¶](#configure-marimos-runtime "Permanent link") [Configure marimo's runtime](https://docs.marimo.io/guides/configuration/runtime_configuration/) to not autorun on startup or on cell execution. Even when autorun is disabled, marimo still tracks dependencies across cells, marking dependents of a cell as stale when you run it. You can click a single button to run all your stale cells and bring your notebook back up-to-date. ### Stop execution with `mo.stop`[¶](#stop-execution-with-mostop "Permanent link") Use [`mo.stop`](https://docs.marimo.io/api/control_flow/#marimo.stop " marimo.stop") to stop a cell from executing if a condition is met: `[](#__codelineno-0-1)# if condition is True, the cell will stop executing after mo.stop() returns [](#__codelineno-0-2)mo.stop(condition) [](#__codelineno-0-3)# this won't be called if condition is True [](#__codelineno-0-4)expensive_function_call()` Use [`mo.stop()`](https://docs.marimo.io/api/control_flow/#marimo.stop " marimo.stop") in conjunction with [`mo.ui.run_button()`](https://docs.marimo.io/api/inputs/run_button/#marimo.ui.run_button " marimo.ui.run_button") to require a button press for expensive cells: ### Working with expensive notebooks[¶](#working-with-expensive-notebooks "Permanent link") For more tips on adapting to marimo's execution model, see our guide on [working with expensive notebooks](https://docs.marimo.io/guides/expensive_notebooks/). Redefining variables[¶](#redefining-variables "Permanent link") --------------------------------------------------------------- marimo "compiles" your notebook cells into a directed graph on cells, linked by variable declarations and references, reusing this graph to run your notebook as a script or app. For marimo's compilation to work, the same variable cannot be defined in multiple cells; otherwise, marimo wouldn't know what order to run cells in. To adapt to the restriction, we suggest: 1. Encapsulating code into functions when possible, to minimize the number of global variables. 2. Prefixing temporary variables with an underscore (`_my_temporary`), which makes the variable **local** to a cell. 3. Mutating variables in the cell that defines them. When working with **dataframes**, you might be used to redefining the same `df` variable in multiple cells. That won't work in marimo. Instead, try merging the cells into a single cell: _Don't_ do this: `[](#__codelineno-2-1)df = pd.DataFrame({"my_column": [1, 2]})` `[](#__codelineno-3-1)df["another_column"] = [3, 4]` _Instead_, do this: `[](#__codelineno-4-1)df = pd.DataFrame({"my_column": [1, 2]}) [](#__codelineno-4-2)df["another_column"] = [3, 4]` If you do need to transform a dataframe across multiple cells, you can alias the dataframe: `[](#__codelineno-5-1)df = pd.DataFrame({"my_column": [1, 2]})` `[](#__codelineno-6-1)augmented_df = df [](#__codelineno-6-2)augmented_df["another_column"] = [3, 4]` marimo's file format[¶](#marimos-file-format "Permanent link") -------------------------------------------------------------- marimo stores notebooks as Python, not JSON. This lets you version notebooks with git, [execute them as scripts](https://docs.marimo.io/guides/scripts/), and import named cells into other Python files. However, it does mean that your notebook outputs (e.g., plots) are not stored in the file. If you'd like to keep a visual record of your notebook work, [enable the "Auto-download as HTML/IPYNB" setting](https://docs.marimo.io/guides/configuration/), which will periodically snapshot your notebook as HTML or IPYNB to a `__marimo__` folder in the notebook directory. ### Converting Jupyter notebooks to marimo notebooks[¶](#converting-jupyter-notebooks-to-marimo-notebooks "Permanent link") Convert Jupyter notebooks to marimo notebooks at the command-line: `[](#__codelineno-7-1)marimo convert your_notebook.ipynb -o your_notebook.py` ### Exporting marimo notebooks to Jupyter notebooks[¶](#exporting-marimo-notebooks-to-jupyter-notebooks "Permanent link") Export to an `ipynb` file with `[](#__codelineno-8-1)marimo export ipynb notebook.py -o notebook.ipynb` Note that some marimo library functions, including UI elements, won't work in Jupyter notebooks. Magic commands[¶](#magic-commands "Permanent link") --------------------------------------------------- Because marimo notebooks are just Python (improving maintainability), marimo doesn't support IPython magic commands or `!`\-prefixed console commands. Here are some alternatives. ### Run console commands with subprocess.run[¶](#run-console-commands-with-subprocessrun "Permanent link") To run a console command, use Python's [subprocess.run](https://docs.python.org/3/library/subprocess.html#subprocess.run): `[](#__codelineno-9-1)import subprocess [](#__codelineno-9-2)[](#__codelineno-9-3)# run: "ls -l" [](#__codelineno-9-4)subprocess.run(["ls", "-l"])` ### Common magic commands replacements[¶](#common-magic-commands-replacements "Permanent link") | Magic Command | Replacement | | --- | --- | | %cd | `os.chdir()`, see also [`mo.notebook_dir()`](https://docs.marimo.io/api/miscellaneous/#marimo.notebook_dir " marimo.notebook_dir") | | %clear | Right-click or toggle the cell actions | | %debug | Python's built-in debugger: `breakpoint()` | | %env | `os.environ` | | %load | N/A - use Python imports | | %load\_ext | N/A | | %autoreload | marimo's [module autoreloader](https://docs.marimo.io/guides/editor_features/module_autoreloading/) | | %matplotlib | marimo auto-displays plots | | %pwd | `os.getcwd()` | | %pip | Use marimo's [built-in package management](https://docs.marimo.io/guides/editor_features/package_management/) | | %who\_ls | `dir()`, `globals()`, [`mo.refs()`](https://docs.marimo.io/api/miscellaneous/#marimo.refs " marimo.refs"), [`mo.defs()`](https://docs.marimo.io/api/miscellaneous/#marimo.defs " marimo.defs") | | %system | `subprocess.run()` | | %%time | `time.perf_counter()` or Python's timeit module | | %%timeit | Python's timeit module | | %%writefile | `with open("file.txt", "w") as f: f.write()` | | %%capture | [`mo.capture_stdout()`](https://docs.marimo.io/api/outputs/#marimo.capture_stdout " marimo.capture_stdout"), [`mo.capture_stderr()`](https://docs.marimo.io/api/outputs/#marimo.capture_stderr " marimo.capture_stderr") | | %%html | [`mo.Html()`](https://docs.marimo.io/api/html/#marimo.Html " marimo.Html dataclass ") or [`mo.md()`](https://docs.marimo.io/api/markdown/#marimo.md " marimo.md") | | %%latex | [`mo.md(r'$$...$$')`](https://docs.marimo.io/api/markdown/#marimo.md " marimo.md") | ### Installing packages with marimo's package manager[¶](#installing-packages-with-marimos-package-manager "Permanent link") Use marimo's package management sidebar panel to install packages to your current environment. Learn more in our [package management guide](https://docs.marimo.io/guides/editor_features/package_management/). Interactive guide[¶](#interactive-guide "Permanent link") --------------------------------------------------------- This guide contains additional tips to help you adapt to marimo. Fun fact: the guide is itself a marimo notebook! Import star - marimo https://docs.marimo.io/guides/understanding_errors/import_star/ Star imports[¶](#star-imports "Permanent link") ----------------------------------------------- You're probably on this page because you just saw an error like this one: marimo raises this error you attempt to use `import *`. In this example, `x` was already defined, and the subsequent cell raised an error when we tried to run it. In your case, perhaps your variable is a loop variable (`i`), a dataframe (`df`), or a plot (`fig`, `ax`). Why can't I use `*` imports?[¶](#why-cant-i-use-imports "Permanent link") ------------------------------------------------------------------------- Star imports are incompatible with marimo's git-friendly file format and reproducible reactive execution: * marimo's Python file format stores code in functions, so notebooks can be imported as regular Python modules without executing all their code. But Python disallows `import *` everywhere except at the top-level of a module. * Star imports would also silently add names to globals, which would be incompatible with [reactive execution](https://docs.marimo.io/guides/reactivity/). Even Python's [official style guide](https://peps.python.org/pep-0008/) discourages the use of `import *`, writing: > Wildcard imports (from import \*) should be avoided, as they make it unclear which names are present in the namespace, confusing both readers and many automated tools. **What do I get in return?** By accepting this constraint on imports, marimo makes your notebooks: * **reproducible**, with a well-defined execution order, no hidden state, and no hidden bugs; * **executable** as a script; * **interactive** with UI elements that work without callbacks; * **shareable as a web app**, with far better performance than streamlit. As a bonus, you'll find that you end up with cleaner, reusable code. How do I fix this error?[¶](#how-do-i-fix-this-error "Permanent link") ---------------------------------------------------------------------- Fixing this error is simple: just import the module, and use `.` notation to access its members. instead of Cycles - marimo https://docs.marimo.io/guides/understanding_errors/cycles/ You're probably on this page because you just saw an error like this one: marimo raises this error when a cell is involved in a cycle on variables. In this example, the first cell declares `a` and reads `b`, while the second cell declares `b` and reads `a`. Why can't I have cycles?[¶](#why-cant-i-have-cycles "Permanent link") --------------------------------------------------------------------- marimo parses your cells to understand the order in which they run: run a cell, and cells that refer to its defined variables need to run afterward. With a cycle, the execution order becomes ambiguous, while also introducing an infinite loop. **What do I get in return?** By accepting this constraint on variables, marimo makes your notebooks: * **reproducible**, with a well-defined execution order, no hidden state, and no hidden bugs; * **executable** as a script; * **interactive** with UI elements that work without callbacks; * **shareable as a web app**, with far better performance than streamlit. As a bonus, you'll find that you end up with cleaner, reusable code. **How do I read the error message?** In the error message, each line says which cell defines a variable and which cell reads a variable. For example, `cell-0 -> a` means `cell-0` defines `a`, and `a -> cell-1` means `cell-1` reads `a`. Similarly, `cell-1 -> b` means `cell-1` defines `b`, and `b -> cell-0` means `cell-0` reads `b`. This creates the cycle from `cell-0 -> cell-1 -> cell-0`. How do I fix this error?[¶](#how-do-i-fix-this-error "Permanent link") ---------------------------------------------------------------------- Cycles usually indicate that your notebook has a bug. Still, you can fix the error by merging the cells involved in the cycle into a single cell: Multiple definitions - marimo https://docs.marimo.io/guides/understanding_errors/multiple_definitions/ You're probably on this page because you just saw an error like this one: marimo raises this error when a variable is defined in multiple cells. In this example, `x` was already defined, and the subsequent cell raised an error when we tried to run it. In your case, perhaps your variable is a loop variable (`i`), a dataframe (`df`), or a plot (`fig`, `ax`). Watch our YouTube explainer Why can't I redefine variables?[¶](#why-cant-i-redefine-variables "Permanent link") ----------------------------------------------------------------------------------- **Understanding the error message.** The error message tells you which other cells defined the multiply defined variable. You can click on the cell name, and marimo will highlight it. **Why can't I redefine variables?** marimo guarantees that the code on the page matches the outputs you see by determining a deterministic execution order on cells; when one cell runs, marimo knows which others should run. But if two cells defined `x`, and a third showed `x`, the output of the third cell would be ambiguous, depending on which of the defining cells ran first (should it be `0` or `1`?). That's a problem because it creates [hidden state and hidden bugs](https://docs.marimo.io/guides/coming_from/jupyter/), and it's part of the reason why [over 96% of Jupyter notebooks on GitHub aren't reproducible](https://leomurta.github.io/papers/pimentel2019a.pdf). **What do I get in return?** By accepting this constraint on variables, marimo makes your notebooks: * **reproducible**, with a well-defined execution order, no hidden state, and no hidden bugs; * **executable** as a script; * **interactive** with UI elements that work without callbacks; * **shareable as a web app**, with far better performance than streamlit. As a bonus, you'll find that you end up with cleaner, reusable code. How do I fix this error?[¶](#how-do-i-fix-this-error "Permanent link") ---------------------------------------------------------------------- You have a few options. ### Use local variables[¶](#use-local-variables "Permanent link") In marimo, variables prefixed with an underscore (`_x` or `_i`) are made local to a cell, and can be redefined across multiple cells. Use this in a pinch, but prefer encapsulating code in functions. ### Encapsulate code in a function[¶](#encapsulate-code-in-a-function "Permanent link") Python provides local scope through functions: if the variable that was redefined is meant to be a temporary variable, then you can make it local to the cell by encapsulating the code in a function. If any of the cell's variables are not meant to be local, or are outputs meant to be displayed, just return them from the function. In general, we recommend writing modular code with meaningful functions. But, in a pinch, just declare an anonymous function like this one to get a "local scope": `[](#__codelineno-1-1)def _(): [](#__codelineno-1-2) import matplotlib.pyplot as plt [](#__codelineno-1-3) fig, ax = plt.subplots() [](#__codelineno-1-4) ax.plot([1, 2]) [](#__codelineno-1-5) return ax [](#__codelineno-1-6)[](#__codelineno-1-7)_()` That's what clicking on the "Fix: Wrap in a function" button does. Note the function `_()` is local to the cell. ### Merge cells[¶](#merge-cells "Permanent link") Often you can simply merge the cells that define the same variable into a single cell. To incrementally show outputs in the cell, use [`mo.output.append`](https://docs.marimo.io/api/outputs/#marimo.output.append " marimo.output.append") or `print()`. ### Chain dataframe methods[¶](#chain-dataframe-methods "Permanent link") When working with dataframes, instead of splitting up operations across multiple cells, chain operations in a single cell. This is especially ergonomic when using [Polars](https://docs.pola.rs/), Daft, or other modern dataframe libraries that support lazy execution. Setup References - marimo https://docs.marimo.io/guides/understanding_errors/setup/ You're probably on this page because you just saw an error like this one: marimo raises this error when the setup cell references variables defined in other cells. In the example above, `image` is defined elsewhere in the notebook, and hence cannot be referenced. Why can't I refer to variables?[¶](#why-cant-i-refer-to-variables "Permanent link") ----------------------------------------------------------------------------------- The setup cell special: it runs before all other cells run, in order to provide symbols that [top-level functions and classes](https://docs.marimo.io/guides/reusing_functions/) can use. That's why it can't reference variables defined by other cells. How do I fix this error?[¶](#how-do-i-fix-this-error "Permanent link") ---------------------------------------------------------------------- Define all needed variables in the setup cell. Or, if this code does not need to run before all other cells (if you are not using top-level functions or classes), simply move your code to a regular cell. SQL, dataframes, and plots - marimo https://docs.marimo.io/guides/working_with_data/ Working with data[¶](#working-with-data "Permanent link") --------------------------------------------------------- These guides introduce you to marimo's features for working with data, including SQL cells, no-code dataframe transformation tools, and plots whose selections are automatically sent back to Python. | Guide | Description | | --- | --- | | [SQL](https://docs.marimo.io/guides/working_with_data/sql/) | Use SQL to query dataframes, databases, CSVs, etc. | | [Dataframes](https://docs.marimo.io/guides/working_with_data/dataframes/) | Filter, search, and transform dataframes without code | | [Plotting](https://docs.marimo.io/guides/working_with_data/plotting/) | Send plot selections to Python | DataFrames - marimo https://docs.marimo.io/guides/working_with_data/dataframes/ Interactive dataframes[¶](#interactive-dataframes "Permanent link") ------------------------------------------------------------------- **marimo makes you more productive when working with dataframes**. * [Display dataframes](#displaying-dataframes) in a rich, interactive table and chart views * [Transform dataframes](#transforming-dataframes) with filters, groupbys, aggregations, and more, **no code required** * [Select data](#selecting-dataframes) from tables or charts and get selections back in Python as dataframes _marimo integrates with [Pandas](https://pandas.pydata.org/) and [Polars](https://pola.rs/) dataframes natively_. Displaying dataframes[¶](#displaying-dataframes "Permanent link") ----------------------------------------------------------------- marimo lets you page through, search, sort, and filter dataframes, making it extremely easy to get a feel for your data. marimo brings dataframes to life. Display dataframes by including them in the last expression of the cell, just like any other object. pandaspolars `[](#__codelineno-0-1)import pandas as pd [](#__codelineno-0-2)[](#__codelineno-0-3)df = pd.read_json( [](#__codelineno-0-4) "https://raw.githubusercontent.com/vega/vega-datasets/master/data/cars.json" [](#__codelineno-0-5)) [](#__codelineno-0-6)df` `[](#__codelineno-1-1)import polars as pl [](#__codelineno-1-2)[](#__codelineno-1-3)df = pl.read_json( [](#__codelineno-1-4) "https://raw.githubusercontent.com/vega/vega-datasets/master/data/cars.json" [](#__codelineno-1-5)) [](#__codelineno-1-6)df` To opt out of the rich dataframe viewer, use [`mo.plain`](https://docs.marimo.io/api/layouts/plain/#marimo.plain " marimo.plain"): pandaspolars `[](#__codelineno-2-1)df = pd.read_json( [](#__codelineno-2-2)"https://raw.githubusercontent.com/vega/vega-datasets/master/data/cars.json" [](#__codelineno-2-3)) [](#__codelineno-2-4)mo.plain(df)` `[](#__codelineno-3-1)df = pl.read_json( [](#__codelineno-3-2)"https://raw.githubusercontent.com/vega/vega-datasets/master/data/cars.json" [](#__codelineno-3-3)) [](#__codelineno-3-4)mo.plain(df)` Transforming dataframes[¶](#transforming-dataframes "Permanent link") --------------------------------------------------------------------- ### No-code transformations[¶](#no-code-transformations "Permanent link") Use [`mo.ui.dataframe`](https://docs.marimo.io/api/inputs/dataframe/#marimo.ui.dataframe " marimo.ui.dataframe") to interactively transform a dataframe with a GUI, no coding required. When you're done, you can copy the code that the GUI generated for you and paste it into your notebook. Build transformations using a GUI pandaspolars `[](#__codelineno-4-1)# Cell 1 [](#__codelineno-4-2)import marimo as mo [](#__codelineno-4-3)import pandas as pd [](#__codelineno-4-4)[](#__codelineno-4-5)df = pd.DataFrame({"person": ["Alice", "Bob", "Charlie"], "age": [20, 30, 40]}) [](#__codelineno-4-6)transformed_df = mo.ui.dataframe(df) [](#__codelineno-4-7)transformed_df` `[](#__codelineno-5-1)# Cell 2 [](#__codelineno-5-2)# transformed_df.value holds the transformed dataframe [](#__codelineno-5-3)transformed_df.value` `[](#__codelineno-6-1)# Cell 1 [](#__codelineno-6-2)import marimo as mo [](#__codelineno-6-3)import polars as pl [](#__codelineno-6-4)[](#__codelineno-6-5)df = pl.DataFrame({"person": ["Alice", "Bob", "Charlie"], "age": [20, 30, 40]}) [](#__codelineno-6-6)transformed_df = mo.ui.dataframe(df) [](#__codelineno-6-7)transformed_df` `[](#__codelineno-7-1)# Cell 2 [](#__codelineno-7-2)# transformed_df.value holds the transformed dataframe [](#__codelineno-7-3)transformed_df.value` Copy the code of the transformation ### Custom filters[¶](#custom-filters "Permanent link") Create custom filters with marimo UI elements, like sliders and dropdowns. pandaspolars `[](#__codelineno-8-1)# Cell 1 - create a dataframe [](#__codelineno-8-2)df = pd.DataFrame({"person": ["Alice", "Bob", "Charlie"], "age": [20, 30, 40]})` `[](#__codelineno-9-1)# Cell 2 - create a filter [](#__codelineno-9-2)age_filter = mo.ui.slider(start=0, stop=100, value=50, label="Max age") [](#__codelineno-9-3)age_filter` `[](#__codelineno-10-1)# Cell 3 - display the transformed dataframe [](#__codelineno-10-2)filtered_df = df[df["age"] < age_filter.value] [](#__codelineno-10-3)mo.ui.table(filtered_df)` `[](#__codelineno-11-1)import marimo as mo [](#__codelineno-11-2)import polars as pl [](#__codelineno-11-3)[](#__codelineno-11-4)df = pl.DataFrame({ [](#__codelineno-11-5) "name": ["Alice", "Bob", "Charlie", "David"], [](#__codelineno-11-6) "age": [25, 30, 35, 40], [](#__codelineno-11-7) "city": ["New York", "London", "Paris", "Tokyo"] [](#__codelineno-11-8)}) [](#__codelineno-11-9)[](#__codelineno-11-10)age_filter = mo.ui.slider.from_series(df["age"], label="Max age") [](#__codelineno-11-11)city_filter = mo.ui.dropdown.from_series(df["city"], label="City") [](#__codelineno-11-12)[](#__codelineno-11-13)mo.hstack([age_filter, city_filter])` `[](#__codelineno-12-1)# Cell 2 [](#__codelineno-12-2)filtered_df = df.filter((pl.col("age") <= age_filter.value) & (pl.col("city") == city_filter.value)) [](#__codelineno-12-3)mo.ui.table(filtered_df)` Select dataframe rows[¶](#selecting-dataframes "Permanent link") ---------------------------------------------------------------- Display dataframes as interactive, [selectable charts](https://docs.marimo.io/guides/working_with_data/plotting/) using [`mo.ui.altair_chart`](https://docs.marimo.io/api/plotting/#marimo.ui.altair_chart " marimo.ui.altair_chart") or [`mo.ui.plotly`](https://docs.marimo.io/api/plotting/#marimo.ui.plotly " marimo.ui.plotly"), or as a row-selectable table with [`mo.ui.table`](https://docs.marimo.io/api/inputs/table/#marimo.ui.table " marimo.ui.table"). Select points in the chart, or select a table row, and your selection is _automatically sent to Python as a subset of the original dataframe_. Select rows in a table, get them back as a dataframe pandaspolars `[](#__codelineno-13-1)# Cell 1 - display a dataframe [](#__codelineno-13-2)import marimo as mo [](#__codelineno-13-3)import pandas as pd [](#__codelineno-13-4)[](#__codelineno-13-5)df = pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]}) [](#__codelineno-13-6)table = mo.ui.table(df, selection="multi") [](#__codelineno-13-7)table` `[](#__codelineno-14-1)# Cell 2 - display the selection [](#__codelineno-14-2)table.value` `[](#__codelineno-15-1)# Cell 1 - display a dataframe [](#__codelineno-15-2)import marimo as mo [](#__codelineno-15-3)import polars as pl [](#__codelineno-15-4)[](#__codelineno-15-5)df = pl.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]}) [](#__codelineno-15-6)table = mo.ui.table(df, selection="multi") [](#__codelineno-15-7)table` `[](#__codelineno-16-1)# Cell 2 - display the selection [](#__codelineno-16-2)table.value` Row viewer panel[¶](#row-viewer-panel "Permanent link") ------------------------------------------------------- To inspect individual rows, you can toggle the Row viewer panel located at the bottom left of a table. While open, click on any row to view. For convenience, you can pin the panel to the side of your editor or toggle the focused cell feature, which ensures the value of the currently focused cell is displayed in the panel. Open the row viewer panel to display data vertically Example notebook[¶](#example-notebook "Permanent link") ------------------------------------------------------- For a comprehensive example of using Polars with marimo, check out our [Polars example notebook](https://github.com/marimo-team/marimo/blob/main/examples/third_party/polars/polars_example.py). Run it with: `[](#__codelineno-17-1)marimo edit https://raw.githubusercontent.com/marimo-team/marimo/main/examples/third_party/polars/polars_example.py` Package management - marimo https://docs.marimo.io/guides/package_management/ The following guides cover how to import, install, and otherwise manage the Python dependencies of your notebooks. | Guide | Description | | --- | --- | | [Importing packages](https://docs.marimo.io/guides/package_management/importing_packages/) | How marimo finds packages on import | | [Installing packages](https://docs.marimo.io/guides/package_management/installing_packages/) | Installing packages with marimo's UI | | [Inlining dependencies](https://docs.marimo.io/guides/package_management/inlining_dependencies/) | Create self-contained notebooks by inlining dependencies in notebook files | | [Using uv](https://docs.marimo.io/guides/package_management/using_uv/) | A guide to using the uv package manager with marimo | Importing packages - marimo https://docs.marimo.io/guides/package_management/importing_packages/ By default, marimo searches for packages in the [virtual environment](https://docs.python.org/3/tutorial/venv.html) it was started in. For example, if you run `[](#__codelineno-0-1)source /path/to/venv/bin/activate [](#__codelineno-0-2)marimo edit my_notebook.py` you'll be able to import packages installed in the environment you just activated. You'll also be able to install packages into this environment using the marimo editor's [package management UI](https://docs.marimo.io/guides/package_management/installing_packages/). Using uv? See our [uv guide](https://docs.marimo.io/guides/package_management/using_uv/) for details on how to use marimo as part of uv projects or as self-contained scripts. Inlining dependencies[¶](#inlining-dependencies "Permanent link") ----------------------------------------------------------------- As an alternative to manually creating and managing a virtual environment, you can let marimo manage virtual environments for you, on a per-notebook basis. If you create a notebook with the `--sandbox` flag — `[](#__codelineno-1-1)marimo edit --sandbox my_notebook.py` — marimo will start your notebook in an isolated environment and keep track of the dependencies you install from the editor. These dependencies are inlined in the notebook file, so that the next time you run the notebook, marimo will run it in an isolated environment with just those dependencies. See our guide on [inlining dependencies](https://docs.marimo.io/guides/package_management/inlining_dependencies/) to learn more. What about kernels? Unlike Jupyter, marimo does not have a concept of "kernels"; notebooks simply use the active virtual environment. The main feature of kernels is to allow different notebooks to depend on different packages, even within the same project. marimo's package sandbox provides this functionality, while also being far simpler to use than custom kernels. Importing local modules[¶](#importing-local-modules "Permanent link") --------------------------------------------------------------------- marimo resolves imports just as Python does: by searching for packages in the directories listed in `sys.path`. That means that in addition to the virtual environment, marimo will search for modules in the directory in which the notebook lives. For example, when you run `[](#__codelineno-2-1)marimo edit /path/to/notebook_dir/notebook.py` marimo will look for modules in `/path/to/notebook_dir`. However, this means that you may need to take additional steps to import modules that live outside this directory. What steps you take depends on whether your code is organized as a Python package. Remember: notebooks are just Python programs In the examples below, notebooks are stored in a separate notebooks directory, which is traditional. However, since marimo notebooks are just Python modules, you can just as well include them in your `src/` directory alongside other Python modules. ### From non-package projects[¶](#from-non-package-projects "Permanent link") You can configure the Python path to accommodate directory structures that look like this: `[](#__codelineno-3-1). [](#__codelineno-3-2)├── notebooks [](#__codelineno-3-3)│   └── my_notebook.py [](#__codelineno-3-4)├── pyproject.toml [](#__codelineno-3-5)└── src [](#__codelineno-3-6) └── my_module.py` In particular, to make `import my_module` work when running editrunscript `[](#__codelineno-4-1)marimo edit notebooks/my_notebook.py` `[](#__codelineno-5-1)marimo run notebooks/my_notebook.py` `[](#__codelineno-6-1)python notebooks/my_notebook.py` add the following configuration to your `pyproject.toml`: pyproject.toml `[](#__codelineno-7-1)[tool.marimo.runtime] [](#__codelineno-7-2)pythonpath = ["src"]` ### From packages[¶](#from-packages "Permanent link") New to Python packages? A [Python package](https://docs.python.org/3/tutorial/modules.html#packages) is a way of structuring Python source files so that the collection of files can be installed in an environment, imported using "dot" notation like `from my_package.my_module import my_function`, and optionally uploaded to package registries like PyPI. If you are new to packages, and find you need to create one, we recommend using [uv](https://docs.astral.sh/uv/) (`uv init --package`). A package has a directory structure like this: `[](#__codelineno-8-1). [](#__codelineno-8-2)├── notebooks [](#__codelineno-8-3)│   └── my_notebook.py [](#__codelineno-8-4)├── pyproject.toml [](#__codelineno-8-5)└── src [](#__codelineno-8-6) └── my_package [](#__codelineno-8-7) ├── __init__.py [](#__codelineno-8-8) └── my_module.py` Say `my_notebook` has a cell with `[](#__codelineno-9-1)from my_package import my_module` Provided that editrunscript `[](#__codelineno-10-1)marimo edit notebooks/my_notebook.py` `[](#__codelineno-11-1)marimo run notebooks/my_notebook.py` `[](#__codelineno-12-1)python notebooks/my_notebook.py` is run from an environment in which your package is installed, marimo will import `my_module` without issue. For example, if you are using `uv`, simply run `[](#__codelineno-13-1)uv run --with marimo marimo edit notebooks/my_notebook.py` Installing packages - marimo https://docs.marimo.io/guides/package_management/installing_packages/ marimo supports package management for `pip`, `uv`, `poetry`, `pixi`, and `rye`. When marimo comes across a module that is not installed, you will be prompted to install it using your preferred package manager. Once the module is installed, all cells that depend on the module will be rerun (or marked as stale). You can also install (and remove) packages using the package manager sidebar panel. Resolving package names We use a heuristic for guessing the package name in your registry (e.g. PyPI) from the module name. It is possible that the package name is different from the module name. If you encounter an error, please [file an issue](https://github.com/marimo-team/marimo/issues) or help us by adding your mapping [directly to the codebase](https://github.com/marimo-team/marimo/blob/main/marimo/_runtime/packages/module_name_to_pypi_name.py). **Notes.** * When using imperative style package managers like `pip`, packages are installed directly in the active virtual environment. * When using `uv`, marimo will decide whether to add it to your `pyproject.toml` (if running as part of a uv project), or whether to install it imperatively with `uv pip` (otherwise). * When running in a [package sandbox](https://docs.marimo.io/guides/package_management/inlining_dependencies/), package installation and removal also updates the notebook's inline dependencies. SQL - marimo https://docs.marimo.io/guides/working_with_data/sql/ Using SQL[¶](#using-sql "Permanent link") ----------------------------------------- marimo lets you mix and match **Python and SQL**: Use SQL to query Python dataframes (or databases like SQLite and Postgres), and get the query result back as a Python dataframe. To create a SQL cell, you first need to install additional dependencies, including [duckdb](https://duckdb.org/): install with pipinstall with uvinstall with conda `[](#__codelineno-0-1)pip install "marimo[sql]"` `[](#__codelineno-2-1)conda install -c conda-forge marimo duckdb polars` Example[¶](#example "Permanent link") ------------------------------------- In this example notebook, we have a Pandas dataframe and a SQL cell that queries it. Notice that the query result is returned as a Python dataframe and usable in subsequent cells. Creating SQL cells[¶](#creating-sql-cells "Permanent link") ----------------------------------------------------------- You can create SQL cells in one of three ways: 1. **Right-click** an "add cell" button ("+" icon) next to a cell and choose "SQL cell" 2. Convert a empty cell to SQL via the cell context menu 3. Click the SQL button that appears at the bottom of the notebook Add SQL Cell This creates a "**SQL**" cell for you, which is syntactic sugar for Python code. The underlying code looks like: `[](#__codelineno-3-1)output_df = mo.sql(f"SELECT * FROM my_table LIMIT {max_rows.value}")` Notice that we have an **`output_df`** variable in the cell. This contains the query result, and is a Polars DataFrame (if you have `polars` installed) or a Pandas DataFrame (if you don't). One of them must be installed in order to interact with the query result. The SQL statement itself is an f-string, letting you interpolate Python values into the query with `{}`. In particular, this means your SQL queries can depend on the values of UI elements or other Python values, and they are fit into marimo's reactive dataflow graph. SQL Output Types[¶](#sql-output-types "Permanent link") ------------------------------------------------------- marimo supports different output types for SQL queries, which is particularly useful when working with large datasets. You can configure this in your application configuration in the top right of the marimo editor. The available options are: * `native`: Uses DuckDB's native lazy relation (recommended for best performance) * `lazy-polars`: Returns a lazy Polars DataFrame * `pandas`: Returns a Pandas DataFrame * `polars`: Returns an eager Polars DataFrame * `auto`: Automatically chooses based on installed packages (first tries `polars` then `pandas`) For best performance with large datasets, we recommend using `native` to avoid loading the entire result set into memory and to more easily chain SQL cells together. By default, only the first 10 rows are displayed in the UI to prevent memory issues. Set a default The default output type is currently `auto`, but we recommend explicitly setting the output type to `native` for best performance with large datasets or `polars` if you need to work with the results in Python code. You can configure this in your application settings. Reference a local dataframe[¶](#reference-a-local-dataframe "Permanent link") ----------------------------------------------------------------------------- You can reference a local dataframe in your SQL cell by using the name of the Python variable that holds the dataframe. If you have a database connection with a table of the same name, the database table will be used instead. Reference a dataframe Since the output dataframe variable (`_df`) has an underscore, making it private, it is not referenceable from other cells. Reference the output of a SQL cell[¶](#reference-the-output-of-a-sql-cell "Permanent link") ------------------------------------------------------------------------------------------- Defining a non-private (non-underscored) output variable in the SQL cell allows you to reference the resulting dataframe in other Python and SQL cells. Reference the SQL result Querying files, databases, and APIs[¶](#querying-files-databases-and-apis "Permanent link") ------------------------------------------------------------------------------------------- In the above example, you may have noticed we queried an HTTP endpoint instead of a local dataframe. We are not only limited to querying local dataframes; we can also query files, databases such as Postgres and SQLite, and APIs: `[](#__codelineno-4-1)-- or [](#__codelineno-4-2)SELECT * FROM 's3://my-bucket/file.parquet'; [](#__codelineno-4-3)-- or [](#__codelineno-4-4)SELECT * FROM read_csv('path/to/example.csv'); [](#__codelineno-4-5)-- or [](#__codelineno-4-6)SELECT * FROM read_parquet('path/to/example.parquet');` For a full list you can check out the [duckdb extensions](https://duckdb.org/docs/extensions/overview). You can also check out our [examples on GitHub](https://github.com/marimo-team/marimo/tree/main/examples/sql). Escaping SQL brackets[¶](#escaping-sql-brackets "Permanent link") ----------------------------------------------------------------- Our "SQL" cells are really just Python under the hood to keep notebooks as pure Python scripts. By default, we use `f-strings` for SQL strings, which allows for parameterized SQL like which allows for parameterized SQL like `SELECT * from table where value < {min}`. To escape real `{`/`}` that you don't want parameterized, use double `{{...}}`: `[](#__codelineno-5-1)SELECT unnest([{{'a': 42, 'b': 84}}, {{'a': 100, 'b': NULL}}]);` Connecting to a custom database[¶](#connecting-to-a-custom-database "Permanent link") ------------------------------------------------------------------------------------- There are two ways to connect to a database in marimo: ### 1\. Using the UI[¶](#1-using-the-ui "Permanent link") Click the "Add Database Connection" button in your notebook to connect to PostgreSQL, MySQL, SQLite, DuckDB, Snowflake, or BigQuery databases. The UI will guide you through entering your connection details securely. Environment variables picked up from your [`dotenv`](https://docs.marimo.io/guides/configuration/runtime_configuration/#environment-variables) can be used to fill out the database configuration fields. Add a database connection through the UI If you'd like to connect to a database that isn't supported by the UI, you can use the code method below, or submit a [feature request](https://github.com/marimo-team/marimo/issues/new?title=New%20database%20connection:&labels=enhancement&template=feature_request.yaml). ### 2\. Using Code[¶](#2-using-code "Permanent link") You can bring your own database via a **connection engine** with one of the following libraries * [SQLAlchemy](https://docs.sqlalchemy.org/en/20/core/connections.html#basic-usage) * [SQLModel](https://sqlmodel.tiangolo.com/tutorial/create-db-and-table/?h=create+engine#create-the-engine) * [Ibis](https://ibis-project.org/backends/athena) * [Custom DuckDB connection](https://duckdb.org/docs/api/python/overview.html#connection-options) * [ClickHouse Connect](https://clickhouse.com/docs/integrations/python#introduction) * [chDB](https://clickhouse.com/docs/chdb) By default, marimo uses the [in-memory duckdb connection](https://duckdb.org/docs/connect/overview.html#in-memory-database). List of supported databases Updated: 2025-04-30. This list is not exhaustive. | Database | Library | | --- | --- | | Amazon Athena | `sqlalchemy`, `sqlmodel`, `ibis` | | Amazon Redshift | `sqlalchemy`, `sqlmodel` | | Apache Drill | `sqlalchemy`, `sqlmodel` | | Apache Druid | `sqlalchemy`, `sqlmodel`, `ibis` | | Apache Hive and Presto | `sqlalchemy`, `sqlmodel` | | Apache Solr | `sqlalchemy`, `sqlmodel` | | BigQuery | `sqlalchemy`, `sqlmodel`, `ibis` | | ClickHouse | `clickhouse_connect`, `chdb` | | CockroachDB | `sqlalchemy`, `sqlmodel` | | Databricks | `sqlalchemy`, `sqlmodel`, `ibis` | | dlt | `ibis` | | Datafusion | `ibis` | | DuckDB | `duckdb` | | EXASolution | `sqlalchemy`, `sqlmodel`, `ibis` | | Elasticsearch (readonly) | `sqlalchemy`, `sqlmodel` | | Firebolt | `sqlalchemy`, `sqlmodel` | | Flink | `ibis` | | Google Sheets | `sqlalchemy`, `sqlmodel` | | Impala | `sqlalchemy`, `sqlmodel`, `ibis` | | Microsoft Access | `sqlalchemy`, `sqlmodel` | | Microsoft SQL Server | `sqlalchemy`, `sqlmodel`, `ibis` | | MonetDB | `sqlalchemy`, `sqlmodel` | | MySQL | `sqlalchemy`, `sqlmodel`, `ibis` | | OpenGauss | `sqlalchemy`, `sqlmodel` | | Oracle | `sqlalchemy`, `sqlmodel`, `ibis` | | PostgreSQL | `sqlalchemy`, `sqlmodel`, `ibis` | | PySpark | `ibis` | | RisingWave | `ibis` | | SAP HANA | `sqlalchemy`, `sqlmodel` | | Snowflake | `sqlalchemy`, `sqlmodel`, `ibis` | | SQLite | `sqlalchemy`, `sqlmodel`, `ibis` | | Teradata Vantage | `sqlalchemy`, `sqlmodel` | | TimePlus | `sqlalchemy`, `sqlmodel` | | Trino | `sqlalchemy`, `sqlmodel`, `ibis` | Define the engine as a Python variable in a cell: SQLAlchemySQLModelIbisDuckDBClickHouse ConnectchDB `[](#__codelineno-6-1)import sqlalchemy [](#__codelineno-6-2)[](#__codelineno-6-3)# Create an in-memory SQLite database with SQLAlchemy [](#__codelineno-6-4)sqlite_engine = sqlachemy.create_engine("sqlite:///:memory:")` `[](#__codelineno-7-1)import sqlmodel [](#__codelineno-7-2)[](#__codelineno-7-3)# Create an in-memory SQLite database with SQLModel [](#__codelineno-7-4)sqlite_engine = sqlmodel.create_engine("sqlite:///:memory:")` `[](#__codelineno-8-1)import ibis [](#__codelineno-8-2)[](#__codelineno-8-3)# Create an in-memory SQLite database with Ibis [](#__codelineno-8-4)sqlite_engine = ibis.connect("sqlite:///:memory:")` `[](#__codelineno-9-1)import duckdb [](#__codelineno-9-2)[](#__codelineno-9-3)# Create a DuckDB connection [](#__codelineno-9-4)duckdb_conn = duckdb.connect("file.db")` ClickHouse Connect enables remote connections to ClickHouse databases. Refer to [the official docs](https://clickhouse.com/docs/integrations/python#gather-your-connection-details) for more configuration options. `[](#__codelineno-10-1)import clickhouse_connect [](#__codelineno-10-2)[](#__codelineno-10-3)engine = clickhouse_connect.get_client(host="localhost", port=8123, username="default", password="password")` Warning chDB is still new. You may experience issues with your queries. We recommend only using one connection at a time. Refer to [chDB docs](https://github.com/orgs/chdb-io/discussions/295) for more information. `[](#__codelineno-11-1)import chdb [](#__codelineno-11-2)[](#__codelineno-11-3)connection = chdb.connect(":memory:") [](#__codelineno-11-4)[](#__codelineno-11-5)# Supported formats with examples: [](#__codelineno-11-6)":memory:" # In-memory database [](#__codelineno-11-7)"test.db" # Relative path [](#__codelineno-11-8)"file:test.db" # Explicit file protocol [](#__codelineno-11-9)"/path/to/test.db" # Absolute path [](#__codelineno-11-10)"file:/path/to/test.db" # Absolute path with protocol [](#__codelineno-11-11)"file:test.db?param1=value1¶m2=value2" # With query parameters [](#__codelineno-11-12)"file::memory:?verbose&log-level=test" # In-memory with parameters [](#__codelineno-11-13)"///path/to/test.db?param1=value1" # Triple slash absolute path` marimo will auto-discover the engine and let you select it in the SQL cell's connection dropdown. Choose a custom database connection Database, schema, and table auto-discovery[¶](#database-schema-and-table-auto-discovery "Permanent link") --------------------------------------------------------------------------------------------------------- marimo will automatically discover the database connection and display the database, schemas, tables, and columns in the Data Sources panel. This panels lets you quickly navigate your database schema and reference tables and columns to pull in your SQL queries. Data Sources panel Note By default, marimo auto-discovers databases and schemas, but not tables and columns (to avoid performance issues with large databases). You can configure this behavior in your `pyproject.toml` file. Options are `true`, `false`, or `"auto"`. `"auto"` will determine whether to auto-discover based on the type of database (e.g. when the value is `"auto"`, Snowflake and BigQuery will not auto-discover tables and columns while SQLite, Postgres, and MySQL will): pyproject.toml `[](#__codelineno-12-1)[tool.marimo.datasources] [](#__codelineno-12-2)auto_discover_schemas = true # Default: true [](#__codelineno-12-3)auto_discover_tables = "auto" # Default: "auto" [](#__codelineno-12-4)auto_discover_columns = "auto" # Default: false` Catalogs[¶](#catalogs "Permanent link") --------------------------------------- marimo supports connecting to Iceberg catalogs. You can click the "+" button in the Datasources panel or manually create a [PyIceberg](https://py.iceberg.apache.org/) `Catalog` connection. PyIceberg supports a variety of catalog implementations including REST, SQL, Glue, DynamoDB, and more. `[](#__codelineno-13-1)from pyiceberg.catalog.rest import RestCatalog [](#__codelineno-13-2)[](#__codelineno-13-3)catalog = RestCatalog( [](#__codelineno-13-4) name="catalog", [](#__codelineno-13-5) warehouse="1234567890", [](#__codelineno-13-6) uri="https://my-catalog.com", [](#__codelineno-13-7) token="my-token", [](#__codelineno-13-8))` Catalogs will appear in the Datasources panel, but they cannot be used as an engine in SQL cells. However, you can still load the table and use it in subsequent Python or SQL cells. `[](#__codelineno-14-1)df = catalog.load_table(("my-namespace", "my-table")).to_polars()` Interactive tutorial[¶](#interactive-tutorial "Permanent link") --------------------------------------------------------------- For an interactive tutorial, run at your command-line. Examples[¶](#examples "Permanent link") --------------------------------------- Check out our [examples on GitHub](https://github.com/marimo-team/marimo/tree/main/examples/sql). Inlining dependencies - marimo https://docs.marimo.io/guides/package_management/inlining_dependencies/ marimo is the only Python notebook that is reproducible down to the packages, letting you inline Python dependencies in notebook files and running notebooks in isolated or "sandboxed" venvs. This lets you share standalone notebooks without shipping `requirements.txt` files alongside them, and guarantees your notebooks will work weeks, months, even years into the future. To opt-in to dependency inlining, use the `sandbox` flag: editrunnew `[](#__codelineno-0-1)marimo edit --sandbox notebook.py` `[](#__codelineno-1-1)marimo run --sandbox notebook.py` When running with `--sandbox`, marimo: 1. tracks the packages and versions used by your notebook, saving them in the notebook file; 2. runs in an isolated virtual environment ("sandbox") that only contains the notebook dependencies. marimo's sandbox provides two key benefits. (1) Notebooks that carry their own dependencies are easy to share — just send the `.py` file. (2) Isolating a notebook from other installed packages prevents obscure bugs. You can also run sandboxed notebooks as scripts: Solving the notebook reproducibility crisis marimo's support for package sandboxing is only possible because marimo notebooks are stored as pure Python files, letting marimo take advantage of new Python standards like [PEP 723](https://peps.python.org/pep-0723/) and tools like uv. In contrast, traditional notebooks like Jupyter are stored as JSON files, and which suffer from a [reproducibility crisis](https://leomurta.github.io/papers/pimentel2019a.pdf) due to the lack of package management. When running with `--sandbox`, marimo automatically tracks package metadata in your notebook file using inline script metadata, which per [PEP 723](https://peps.python.org/pep-0723/) is essentially a pyproject.toml inlined as the script's header. This metadata is used to manage the notebook's dependencies and Python version, and looks something like this: `[](#__codelineno-4-1)# /// script [](#__codelineno-4-2)# requires-python = ">=3.11" [](#__codelineno-4-3)# dependencies = [ [](#__codelineno-4-4)# "pandas==", [](#__codelineno-4-5)# "altair==", [](#__codelineno-4-6)# ] [](#__codelineno-4-7)# ///` Example notebooks The [example notebooks](https://github.com/marimo-team/marimo/tree/main/examples) in our GitHub repo were all created using `--sandbox`. Take a look at any of them for an example of the full script metadata. ### Adding and removing packages[¶](#adding-and-removing-packages "Permanent link") **Using the marimo editor.** When you import a module in the marimo editor, if marimo detects that it is a third-party package, it will automatically be added to the script metadata. Removing an import does _not_ remove it from the script metadata (since library code may still use the package). Adding packages via the package manager panel will also add packages to script metadata, and removing packages from the panel will in turn remove them from the script metadata. **Adding packages manually.** You can manually manage your notebook's requirements: * edit the script metadata manually in an editor like VS Code or neovim. * use `uv` from the command-line: `[](#__codelineno-5-1)uv add --script notebook.py numpy` `[](#__codelineno-6-1)uv remove --script notebook.py numpy` ### Package locations[¶](#package-locations "Permanent link") By default, marimo will look for packages on PyPI. You can edit the script metadata to look for packages elsewhere, such as on GitHub. Consult the [Python packaging documentation](https://packaging.python.org/en/latest/specifications/dependency-specifiers/#examples) for more information. ### Local development with editable installs[¶](#local-development-with-editable-installs "Permanent link") When developing a local package, you can install it in editable mode using the `[tool.uv.sources]` section in the script metadata. For example: `[](#__codelineno-7-1)# /// script [](#__codelineno-7-2)# requires-python = ">=3.11" [](#__codelineno-7-3)# dependencies = [ [](#__codelineno-7-4)# "my-package", [](#__codelineno-7-5)# ] [](#__codelineno-7-6)# [](#__codelineno-7-7)# [tool.uv.sources] [](#__codelineno-7-8)# my-package = { path = "../", editable = true } [](#__codelineno-7-9)# ///` This is particularly useful when you want to test changes to your package without reinstalling it. The package will be installed in "editable" mode, meaning changes to the source code will be reflected immediately in your notebook. ### Specifying alternative package indexes[¶](#specifying-alternative-package-indexes "Permanent link") When you need to use packages from a custom PyPI server or alternative index, you can specify these in your script metadata using the `[[tool.uv.index]]` section. This is useful for private packages or when you want to use packages from a specific source. `[](#__codelineno-8-1)# /// script [](#__codelineno-8-2)# requires-python = ">=3.11" [](#__codelineno-8-3)# dependencies = [ [](#__codelineno-8-4)# "pandas==", [](#__codelineno-8-5)# "private-package==", [](#__codelineno-8-6)# ] [](#__codelineno-8-7)# [](#__codelineno-8-8)# [[tool.uv.index]] [](#__codelineno-8-9)# name = "custom-index" [](#__codelineno-8-10)# url = "https://custom-pypi-server.example.com/simple/" [](#__codelineno-8-11)# explicit = true [](#__codelineno-8-12)# [](#__codelineno-8-13)# [tool.uv.sources] [](#__codelineno-8-14)# private-package = { index = "custom-index" } [](#__codelineno-8-15)# ///` In this example: * `[[tool.uv.index]]` defines a custom package index * `name` is an identifier for the index * `url` points to your custom PyPI server * `explicit = true` means this index will only be used for packages explicitly associated with it * `[tool.uv.sources]` specifies which packages should come from which indexes This approach ensures that specific packages are always fetched from your designated custom index, while other packages continue to be fetched from the default PyPI repository. Configuration[¶](#configuration "Permanent link") ------------------------------------------------- Running marimo in a sandbox environment uses `uv` to create an isolated virtual environment. You can use any of `uv`'s [supported environment variables](https://docs.astral.sh/uv/configuration/environment/). ### Choosing the Python version[¶](#choosing-the-python-version "Permanent link") For example, you can specify the Python version using the `UV_PYTHON` environment variable: `[](#__codelineno-9-1)UV_PYTHON=3.13 marimo edit --sandbox notebook.py` ### Other common configuration[¶](#other-common-configuration "Permanent link") Another common configuration is `uv`'s link mode: `[](#__codelineno-10-1)UV_LINK_MODE="copy" marimo edit --sandbox notebook.py` Sharing on the web[¶](#sharing-on-the-web "Permanent link") ----------------------------------------------------------- You can also upload sandboxed notebooks to the web, such as on GitHub, and have others run them locally with a single command: `[](#__codelineno-11-1)uvx marimo edit --sandbox https://gist.githubusercontent.com/kolibril13/a59135dd0973b97d488ba21c650667fe/raw/5f98021b5d3c024d5827fa9464787517495178b4/marimo_minimal_numpy_example.py` **Note:** 1. This command will run code from a URL. Make sure you trust the source before proceeding. 2. Upon execution, you’ll be prompted: `[](#__codelineno-12-1)Would you like to run it in a secure docker container? [Y/n]:` To proceed securely, ensure you have [Docker](https://www.docker.com/) installed and running, then press `Y`. Specifying dependencies in Markdown files[¶](#specifying-dependencies-in-markdown-files "Permanent link") --------------------------------------------------------------------------------------------------------- Sandboxing support is also provided in [marimo's markdown file format](https://docs.marimo.io/guides/editor_features/watching/#as-markdown) under the `pyproject` entry of your frontmatter. `[](#__codelineno-13-1)--- [](#__codelineno-13-2)title: My Notebook [](#__codelineno-13-3)marimo-version: 0.0.0 [](#__codelineno-13-4)pyproject: | [](#__codelineno-13-5) requires-python: ">=3.11" [](#__codelineno-13-6) dependencies: [](#__codelineno-13-7) - pandas== [](#__codelineno-13-8) - altair== [](#__codelineno-13-9)---` Using uv - marimo https://docs.marimo.io/guides/package_management/using_uv/ [uv](https://docs.astral.sh/uv/) is an extremely fast Python package and project manager: you can use it to install packages, manage the dependencies of Python projects, and run scripts. While marimo supports all major package managers, it integrates especially tightly with uv. In particular, marimo's package sandbox feature, which lets you [inline dependencies](https://docs.marimo.io/guides/package_management/inlining_dependencies/) in notebook files, requires uv. No prior knowledge required This guide teaches you the basics of using `uv` with marimo. It assumes zero familiarity with `uv`. You can manage your notebooks' dependencies in three different ways: 1. inline dependencies: [inlining dependencies](https://docs.marimo.io/guides/package_management/inlining_dependencies/) in notebook files, using `marimo edit --sandbox notebook.py` 2. projects: using a `uv` project , which define dependencies declaratively in a `pyproject.toml` file; 3. non-project environment: dependencies are imperatively installed We'll walk through each of these three ways in this guide. Using inline dependencies[¶](#using-inline-dependencies "Permanent link") ------------------------------------------------------------------------- The easiest way to get started is to use marimo's [package sandbox feature](https://docs.marimo.io/guides/package_management/inlining_dependencies/), which manages your dependencies for you. Create or edit your notebook with [`uvx` command](https://docs.astral.sh/uv/concepts/tools/#the-uv-tool-interface), making sure to include the `--sandbox` flag: `[](#__codelineno-0-1)uvx marimo edit --sandbox my_notebook.py` This command installs marimo in a temporary environment, activates it, then runs your marimo notebook. The `--sandbox` flag what tells marimo to keep track of your dependencies and store them in the notebook file. If there are any dependencies already tracked in the file, this command will download them and install them in the environment. Run sandboxed notebooks as scripts with ### From URLs[¶](#from-urls "Permanent link") You can also upload sandboxed notebooks to the web, such as on GitHub, and have others run them locally with a single command: `[](#__codelineno-2-1)uvx marimo edit --sandbox https://gist.githubusercontent.com/kolibril13/a59135dd0973b97d488ba21c650667fe/raw/5f98021b5d3c024d5827fa9464787517495178b4/marimo_minimal_numpy_example.py` **Note:** 1. This command will run code from a URL. Make sure you trust the source before proceeding. 2. Upon execution, you’ll be prompted: `[](#__codelineno-3-1)Would you like to run it in a secure docker container? [Y/n]:` To proceed securely, ensure you have [Docker](https://www.docker.com/) installed and running, then press `Y`. To learn more, read our full guide on using [inline dependencies](https://docs.marimo.io/guides/package_management/inlining_dependencies/). Using uv projects[¶](#using-uv-projects "Permanent link") --------------------------------------------------------- A [`uv` project](https://docs.astral.sh/uv/guides/projects/) is a directory in which you can store Python code, including notebooks, alongside a pyproject.toml file that declares the project's dependencies. ### Creating a project[¶](#creating-a-project "Permanent link") Create a project with `uv init`: `[](#__codelineno-4-1)uv init hello-world [](#__codelineno-4-2)cd hello-world` This creates a pyproject.toml and some starter code. Next, add marimo to your project: Omitting marimo from your project Adding marimo to your project is optional. Instead, you can run marimo in a temporary environment that has access to your project's dependencies using `uv run --with marimo marimo edit`. ### Running marimo[¶](#running-marimo "Permanent link") Once you've added marimo, use the `uv run` command to run the version of marimo installed in your project: `[](#__codelineno-6-1)uv run marimo edit my_notebook.py` Starting marimo in this way will let marimo import any of the packages installed in your project. **Scripts.** Run marimo notebooks as scripts with which will run your notebook in an environment containing your project dependencies. ### Adding and removing dependencies[¶](#adding-and-removing-dependencies "Permanent link") #### Using the uv command-line[¶](#using-the-uv-command-line "Permanent link") Use `uv add` to add dependencies: You can also specify a version Remove packages with `uv remove`: #### Using the marimo editor[¶](#using-the-marimo-editor "Permanent link") If you started marimo with `uv run marimo edit`, the marimo editor's [package management features](https://docs.marimo.io/guides/package_management/installing_packages/) will add and remove packages from your pyproject.toml, so there's no need to use the `uv` command-line if you don't want to. Using marimo in a non-project environment[¶](#using-marimo-in-a-non-project-environment "Permanent link") --------------------------------------------------------------------------------------------------------- If you are used to a venv and pip based workflow, you can use the `uv venv` and `uv pip` commands for a similar but more performant experience: * `uv venv` creates a virtual environment in the current directory, at `.venv` * `uv pip` lets you install and uninstall packages in the venv ### Example[¶](#example "Permanent link") `[](#__codelineno-11-1)$ uv venv [](#__codelineno-11-2)$ uv pip install numpy [](#__codelineno-11-3)$ uv pip install marimo [](#__codelineno-11-4)$ uv run marimo edit` From here, `import numpy` will work within the notebook, and marimo's UI installer will add packages to the environment with `uv pip install` on your behalf. Generate with AI - marimo https://docs.marimo.io/guides/generate_with_ai/ Generate notebooks with AI[¶](#generate-notebooks-with-ai "Permanent link") --------------------------------------------------------------------------- | Guide | Description | | --- | --- | | [Zero-shot entire notebooks](https://docs.marimo.io/guides/generate_with_ai/text_to_notebook/) | Generate entire notebooks with AI | | [AI-assisted coding](https://docs.marimo.io/guides/editor_features/ai_completion/) | Refactor code and generate cells with AI | Plotting - marimo https://docs.marimo.io/guides/working_with_data/plotting/ marimo supports most major plotting libraries, including Matplotlib, Seaborn, Plotly, Altair, and HoloViews. Just import your plotting library of choice and use it as you normally would. For Altair and Plotly plots, marimo does something special: use [`mo.ui.altair_chart`](https://docs.marimo.io/api/plotting/#marimo.ui.altair_chart " marimo.ui.altair_chart") or [`mo.ui.plotly`](https://docs.marimo.io/api/plotting/#marimo.ui.plotly " marimo.ui.plotly") to connect frontend selections to Python! Reactive plots! marimo supports reactive plots via [`mo.ui.altair_chart`](https://docs.marimo.io/api/plotting/#marimo.ui.altair_chart " marimo.ui.altair_chart") and [`mo.ui.plotly`](https://docs.marimo.io/api/plotting/#marimo.ui.plotly " marimo.ui.plotly")! Select and filter with your mouse, and marimo _automatically makes the selected data available in Python as a Pandas dataframe_! Reactive plots! ⚡[¶](#reactive-plots "Permanent link") ------------------------------------------------------ Requirements Reactive plots currently require Altair or Plotly. Install with `pip install altair` or `pip install plotly`, depending on which library you are using. Selections in plotly are limited to scatter plots, treemaps charts, and sunbursts charts, while Altair supports a larger class of plots for selections. ### Altair[¶](#altair "Permanent link") Use [`mo.ui.altair_chart`](https://docs.marimo.io/api/plotting/#marimo.ui.altair_chart " marimo.ui.altair_chart") to easily create interactive, selectable plots: _selections you make on the frontend are automatically made available as Pandas dataframes in Python._ Wrap an Altair chart in [`mo.ui.altair_chart`](https://docs.marimo.io/api/plotting/#marimo.ui.altair_chart " marimo.ui.altair_chart") to make it **reactive**: select data on the frontend, access it via the chart's `value` attribute (`chart.value`). #### Disabling automatic selection[¶](#disabling-automatic-selection "Permanent link") marimo automatically adds a default selection based on the mark type, however, you may want to customize the selection behavior of your Altair chart. You can do this by setting `chart_selection` and `legend_selection` to `False`, and using `.add_params` directly on your Altair chart. `[](#__codelineno-1-1)# Create an interval selection [](#__codelineno-1-2)brush = alt.selection_interval(encodings=["x"]) [](#__codelineno-1-3)[](#__codelineno-1-4)_chart = ( [](#__codelineno-1-5) alt.Chart(traces, height=150) [](#__codelineno-1-6) .mark_line() [](#__codelineno-1-7) .encode(x="index:Q", y="value:Q", color="traces:N") [](#__codelineno-1-8) .add_params(brush) # add the selection to the chart [](#__codelineno-1-9)) [](#__codelineno-1-10)[](#__codelineno-1-11)chart = mo.ui.altair_chart( [](#__codelineno-1-12) _chart, [](#__codelineno-1-13) # disable automatic selection [](#__codelineno-1-14) chart_selection=False, [](#__codelineno-1-15) legend_selection=False [](#__codelineno-1-16)) [](#__codelineno-1-17)chart # You can now access chart.value to get the selected data` _Reactive plots are just one way that marimo **makes your data tangible**._ #### Example[¶](#example "Permanent link") `[](#__codelineno-2-1)import marimo as mo [](#__codelineno-2-2)import altair as alt [](#__codelineno-2-3)import vega_datasets [](#__codelineno-2-4)[](#__codelineno-2-5)# Load some data [](#__codelineno-2-6)cars = vega_datasets.data.cars() [](#__codelineno-2-7)[](#__codelineno-2-8)# Create an Altair chart [](#__codelineno-2-9)chart = alt.Chart(cars).mark_point().encode( [](#__codelineno-2-10) x='Horsepower', # Encoding along the x-axis [](#__codelineno-2-11) y='Miles_per_Gallon', # Encoding along the y-axis [](#__codelineno-2-12) color='Origin', # Category encoding by color [](#__codelineno-2-13)) [](#__codelineno-2-14)[](#__codelineno-2-15)# Make it reactive ⚡ [](#__codelineno-2-16)chart = mo.ui.altair_chart(chart)` `[](#__codelineno-3-1)# In a new cell, display the chart and its data filtered by the selection [](#__codelineno-3-2)mo.vstack([chart, chart.value.head()])` #### Learning Altair[¶](#learning-altair "Permanent link") If you're new to **Altair**, we highly recommend exploring the [Altair documentation](https://altair-viz.github.io/). Altair provides a declarative, concise, and simple way to create highly interactive and sophisticated plots. Altair is based on [Vega-Lite](https://vega.github.io/vega-lite/), an exceptional tool for creating interactive charts that serves as the backbone for marimo's reactive charting capabilities. ##### Concepts[¶](#concepts "Permanent link") Learn by doing? Skip this section! This section summarizes the main concepts used by Altair (and Vega-Lite). Feel free to skip this section and return later. Our choice to use the Vega-Lite specification was driven by its robust data model, which is well-suited for data analysis. Some key concepts are summarized below. (For a more detailed explanation, with examples, we recommend the [Basic Statistical Visualization](https://altair-viz.github.io/getting_started/starting.html) tutorial from Altair.) * **Data Source**: This is the information that will be visualized in the chart. It can be provided in various formats such as a dataframe, a list of dictionaries, or a URL pointing to the data source. * **Mark Type**: This refers to the visual representation used for each data point on the chart. The options include 'bar', 'dot', 'circle', 'area', and 'line'. Each mark type offers a different way to visualize and interpret the data. * **Encoding**: This is the process of mapping various aspects or dimensions of the data to visual characteristics of the marks. Encodings can be of different types: * **Positional Encodings**: These are encodings like 'x' and 'y' that determine the position of the marks in the chart. * **Categorical Encodings**: These are encodings like 'color' and 'shape' that categorize data points. They are typically represented in a legend for easy reference. * **Transformations**: These are operations that can be applied to the data before it is visualized, for example, filtering and aggregation. These transformations allow for more complex and nuanced visualizations. **Automatically interactive.** marimo adds interactivity automatically, based on the mark used and the encodings. For example, if you use a `mark_point` and an `x` encoding, marimo will automatically add a brush selection to the chart. If you add a `color` encoding, marimo will add a legend and a click selection. #### Automatic Selections[¶](#automatic-selections "Permanent link") By default [`mo.ui.altair_chart`](https://docs.marimo.io/api/plotting/#marimo.ui.altair_chart " marimo.ui.altair_chart") will make the chart and legend selectable. Depending on the mark type, the chart will either have a `point` or `interval` ("brush") selection. When using non-positional encodings (color, size, etc), [`mo.ui.altair_chart`](https://docs.marimo.io/api/plotting/#marimo.ui.altair_chart " marimo.ui.altair_chart") will also make the legend selectable. Selection configurable through `*_selection` params in [`mo.ui.altair_chart`](https://docs.marimo.io/api/plotting/#marimo.ui.altair_chart " marimo.ui.altair_chart"). See the [API docs](https://docs.marimo.io/api/plotting/#marimo.ui.altair_chart " marimo.ui.altair_chart") for details. Note You may still add your own selection parameters via Altair or Vega-Lite. marimo will not override your selections. #### Altair transformations[¶](#altair-transformations "Permanent link") Altair supports a variety of transformations, such as filtering, aggregation, and sorting. These transformations can be used to create more complex and nuanced visualizations. For example, you can use a filter to show only the points that meet a certain condition, or use an aggregation to show the average value of a variable. In order for marimo's reactive plots to work with transformations, you must install `vegafusion`, as this feature uses `chart.transformed_data` (which requires version 1.4.0 or greater of the `vegafusion` packages). `[](#__codelineno-4-1)# These can be installed with pip using: [](#__codelineno-4-2)pip install "vegafusion[embed]>=1.4.0" [](#__codelineno-4-3)# Or with conda using: [](#__codelineno-4-4)conda install -c conda-forge "vegafusion-python-embed>=1.4.0" "vegafusion>=1.4.0"` ### Plotly[¶](#plotly "Permanent link") mo.ui.plotly only supports scatter plots, treemaps charts, and sunbursts charts marimo can render any Plotly plot, but [`mo.ui.plotly`](https://docs.marimo.io/api/plotting/#marimo.ui.plotly " marimo.ui.plotly") only supports reactive selections for scatter plots, treemaps charts, and sunbursts charts. If you require other kinds of selection, consider using [`mo.ui.altair_chart`](https://docs.marimo.io/api/plotting/#marimo.ui.altair_chart " marimo.ui.altair_chart"). Use [`mo.ui.plotly`](https://docs.marimo.io/api/plotting/#marimo.ui.plotly " marimo.ui.plotly") to create selectable Plotly plots whose values are sent back to Python on selection. matplotlib[¶](#matplotlib "Permanent link") ------------------------------------------- To output a matplotlib plot in a cell's output area, include its `Axes` or `Figure` object as the last expression in your notebook. For example: ``[](#__codelineno-6-1)plt.plot([1, 2]) [](#__codelineno-6-2)# plt.gca() gets the current `Axes` [](#__codelineno-6-3)plt.gca()`` or `[](#__codelineno-7-1)fig, ax = plt.subplots() [](#__codelineno-7-2)[](#__codelineno-7-3)ax.plot([1, 2]) [](#__codelineno-7-4)ax` If you want to output the plot in the console area, use `plt.show()` or `fig.show()`. ### Interactive plots[¶](#interactive-plots "Permanent link") To make matplotlib plots interactive, use [mo.mpl.interactive](https://docs.marimo.io/api/plotting/#marimo.mpl.interactive " marimo.mpl.interactive"). (Matplotlib plots are not yet reactive.) Generate entire notebooks - marimo https://docs.marimo.io/guides/generate_with_ai/text_to_notebook/ Use [`marimo new`](https://docs.marimo.io/cli/#marimo-new) at the command line to generate entirely new notebooks using an LLM. For example, type `[](#__codelineno-0-1)marimo new "Plot an interactive 3D surface with matplotlib."` to open a freshly generated notebook in your browser. For long prompts, you can pass a text file instead: marimo's AI knows how to use marimo-specific UI elements and popular libraries for working with data. To get inspired, visit [https://marimo.app/ai](https://marimo.app/ai). Some of our favorites: * [Dimensionality reduction](https://marimo.app/ai?q=Show+me+how+to+visualize+handwritten+digits+in+two+dimensions%2C+using+an+Altair+scatterplot.+Include+a+cell+that+shows+the+chart+value.+Make+the+chart+render+as+a+square.) * [Smooth a time series](https://marimo.app/ai?q=Show+me+how+to+smooth+time+series+data+and+plot+it.+Use+a+well-known+stock+dataset+and+make+it+interactive) * [Compute code complexity](https://marimo.app/ai?q=Build+a+tool+that+analyzes+Python+code+complexity+metrics+like+cyclomatic+complexity.+Let+me+input+code+snippets+and+see+visualizations+of+the+results.) * [Visualize sorting algorithms](https://marimo.app/ai?q=Plot+an+interesting+3D+surface+with+matplotlib.+Include+an+interactive+element+to+control+the+shape+of+the+surface.) Editor features - marimo https://docs.marimo.io/guides/editor_features/ The **marimo editor** is the browser-based IDE in which you write marimo notebooks. We've taken a batteries-included approach to designing the editor: it comes _packed_ with features to make you productive when working with code and data. | Guide | Description | | --- | --- | | [Overview](https://docs.marimo.io/guides/editor_features/overview/) | An overview of editor features and configuration | | [Package Management](https://docs.marimo.io/guides/editor_features/package_management/) | Using package managers in marimo | | [AI Completion](https://docs.marimo.io/guides/editor_features/ai_completion/) | Code with the help of a language model | | [Language Server](https://docs.marimo.io/guides/editor_features/language_server/) | Code intelligence via LSP | | [Hotkeys](https://docs.marimo.io/guides/editor_features/hotkeys/) | Our hotkeys | Highlights include: * a variables panel that lets you explore variable values and see where they are defined * a data explorer that lets you inspect dataframes and tables at a glance * smart module autoreloading that tells you which cells need to be rerun * code completion * GitHub Copilot * language-model assisted coding * language server protocol (LSP) for diagnostics and code intelligence * vim keybindings * live documentation preiews as you type and much more. Editor overview - marimo https://docs.marimo.io/guides/editor_features/overview/ This guide introduces some of marimo editor's features, including a variables panel, dependency graph viewer, table of contents, HTML export, GitHub copilot, code formatting, a feedback form, and more. Configuration[¶](#configuration "Permanent link") ------------------------------------------------- The editor exposes of a number of settings for the current notebook, as well as user-wide configuration that will apply to all your notebooks. These settings include the option to display the current notebook in full width, to use vim keybindings, to enable GitHub copilot, and more. To access these settings, click the gear icon in the top-right of the editor: A non-exhaustive list of settings: * Outputs above or below code cells * [Disable/enable autorun](https://docs.marimo.io/guides/reactivity/#configuring-how-marimo-runs-cells) * Package installation * [Vim keybindings](#vim-keybindings) * Dark mode * Auto-save * Auto-complete * Editor font-size * Code formatting with ruff/black * [GitHub Copilot](https://docs.marimo.io/guides/editor_features/ai_completion/) * [LLM coding assistant](https://docs.marimo.io/guides/editor_features/ai_completion/) * [Module autoreloading](https://docs.marimo.io/guides/configuration/runtime_configuration/#on-module-change) ### Vim keybindings[¶](#vim-keybindings "Permanent link") marimo supports vim keybindings. **Additional bindings/features:** * `gd` - go to definition * `dd` - when a cell is empty, delete it * `:w` - to save the notebook **Custom vimrc:** You can customize your vim experience by adding a `.vimrc` configuration in the user settings or pyproject.toml User configpyproject.toml marimo.toml `[](#__codelineno-0-1)[keymap] [](#__codelineno-0-2)vimrc = /User/absolute/path/to/.vimrc` pyproject.toml `[](#__codelineno-1-1)[tool.marimo.keymap] [](#__codelineno-1-2)vimrc = relative/path/.vimrc` Overview panels[¶](#overview-panels "Permanent link") ----------------------------------------------------- marimo ships with the IDE panels that provide an overview of your notebook * **file explorer**: view the file tree, open other notebooks * **variables**: explore variable values, see where they are defined and used, with go-to-definition * **data explorer**: see dataframe and table schemas at a glance * **dependency graph**: view dependencies between cells, drill-down on nodes and edges * **package manager**: add and remove packages, and view your current environment * **table of contents**: corresponding to your markdown * **documentation** - move your text cursor over a symbol to see its documentation * **logs**: a continuous stream of stdout and stderr * **scratchpad**: a scratchpad cell where you can execute throwaway code * **snippets** - searchable snippets to copy directly into your notebook * **feedback** - share feedback! These panels can be toggled via the buttons in the left of the editor. Cell actions[¶](#cell-actions "Permanent link") ----------------------------------------------- Click the three dots in the top right of a cell to pull up a context menu, letting you format code, hide code, send a cell to the top or bottom of the notebook, give the cell a name, and more. Drag a cell using the vertical dots to the right of the cell. marimo supports context-sensitive right-click menus in various locations of the editor. Right-click on a cell to open a context-sensitive menu; right click on the create-cell button (the plus icon) to get options for the cell type to create. Go-to-definition[¶](#go-to-definition "Permanent link") ------------------------------------------------------- * Click on a variable in the editor to see where it's defined and used * `Cmd/Ctrl-Click` on a variable to jump to its definition * Right-click on a variable to see a context menu with options to jump to its definition Keyboard shortcuts[¶](#keyboard-shortcuts "Permanent link") ----------------------------------------------------------- We've kept some well-known [keyboard shortcuts](https://docs.marimo.io/guides/editor_features/hotkeys/) for notebooks (`Ctrl-Enter`, `Shift-Enter`), dropped others, and added a few of our own. Hit `Ctrl/Cmd-Shift-H` to pull up the shortcuts. We know keyboard shortcuts are very personal; you can remap them in the configuration. _Missing a shortcut? File a [GitHub issue](https://github.com/marimo-team/marimo/issues)._ Command palette[¶](#command-palette "Permanent link") ----------------------------------------------------- Hit `Cmd/Ctrl+K` to open the command palette. Quickly access common commands with the command palette. _Missing a command? File a [GitHub issue](https://github.com/marimo-team/marimo/issues)._ Editor widths[¶](#editor-widths "Permanent link") ------------------------------------------------- You can set the width of the editor in the notebook settings: * **Compact**: A narrow width with generous margins, ideal for reading * **Wide**: A wider layout that gives more space for content * **Full**: Uses the full width of your browser window, ideal for dashboard-style notebooks * **Multi-column**: Splits your notebook into multiple columns, letting you view and edit cells side-by-side. This is only possible because marimo models your notebook as a directed acyclic graph (DAG) and the [execution order](https://docs.marimo.io/guides/reactivity/#execution-order) is determined by the relationships between cells and their variables, not by the order of cells on the page. Multi-column notebook Get a link to share your notebook via our [online playground](https://docs.marimo.io/guides/wasm/): _Our online playground uses WebAssembly. Most but not all packages on PyPI are supported. Local files are not synchronized to our playground._ Export to static HTML[¶](#export-to-static-html "Permanent link") ----------------------------------------------------------------- Export the current view your notebook to static HTML via the notebook menu: Download as static HTML. You can also export to HTML at the command-line: `[](#__codelineno-2-1)marimo export html notebook.py -o notebook.html` Send feedback[¶](#send-feedback "Permanent link") ------------------------------------------------- The question mark icon in the panel tray opens a dialog to send anonymous feedback. We welcome any and all feedback, from the tiniest quibbles to the biggest blue-sky dreams. Send anonymous feedback with our feedback form. If you'd like your feedback to start a conversation (we'd love to talk with you!), please consider posting in our [GitHub issues](https://github.com/marimo-team/marimo/issues) or [Discord](https://marimo.io/discord?ref=docs). But if you're in a flow state and can't context switch out, the feedback form has your back. AI-assisted coding - marimo https://docs.marimo.io/guides/editor_features/ai_completion/ marimo is an AI-native editor, with support for full-cell AI code generation: * generating new cells from a prompt * refactoring existing cells from a prompt * generating entire notebooks as well as inline copilots (like GitHub Copilot). marimo's AI assistant is specialized for working with data: unlike traditional assistants that only have access to the text of your program, marimo's assistant has access to the values of variables in memory, letting it code against your dataframe and database schemas. This guide provides an overview of these features and how to configure them. Locating your marimo.toml config file Various instructions in this guide refer to the marimo.toml configuration file. Locate this file with `marimo config show | head`. Generating cells with AI[¶](#generating-cells-with-ai "Permanent link") ----------------------------------------------------------------------- marimo has built-in support for generating and refactoring code with LLMs. marimo works with hosted AI providers, such as OpenAI, Anthropic, and Google, as well as local models served via Ollama. **Enabling AI code generation.** To enable AI code generation, first install required dependencies through the notebook settings. Install required dependencies for AI generation through the notebook settings. Then configure your LLM provider through the AI tab in the settings menu; see the section on [connecting your LLM](#connecting-to-an-llm) for detailed instructions. ### Variable context[¶](#variable-context "Permanent link") marimo's AI assistant has your notebook code as context. You can additionally pass variables and their values to the assistant by referencing them by name with `@`. For example, to include the columns of a dataframe `df` in your prompt, write `@df`. Pass variables to your prompt by tagging them with \`@\`. ### Refactor existing cells[¶](#refactor-existing-cells "Permanent link") Make edits to an existing cell by hitting `Ctrl/Cmd-shift-e`, which opens a prompt box that has your cell's code as input. Use AI to modify a cell by pressing \`Ctrl/Cmd-Shift-e\`. ### Generate new cells[¶](#generate-new-cells "Permanent link") #### Generate with AI button[¶](#generate-with-ai-button "Permanent link") At the bottom of every notebook is a button titled "Generate with AI". Click this button to add entirely new cells to your notebook. #### Chat panel[¶](#chat-panel "Permanent link") The chat panel on the left sidebar lets you chat with an LLM and ask questions aboutyour notebook. The LLM can also generate code cells that you can insert into your notebook. See the chat panel in action ### Generating entire notebooks[¶](#generating-entire-notebooks "Permanent link") Generate entire notebooks with `marimo new PROMPT` at the command-line; see the [text-to-notebook docs](https://docs.marimo.io/guides/generate_with_ai/text_to_notebook/) to learn more. ### Custom rules[¶](#custom-rules "Permanent link") You can customize how the AI assistant behaves by adding rules in the marimo settings. These rules help ensure consistent code generation across all AI providers. You can find more information about marimo's supported plotting libraries and data handling in the [plotting guide](https://docs.marimo.io/guides/working_with_data/plotting/#plotting) and [working with data guide](https://docs.marimo.io/guides/working_with_data/). Configure custom AI rules in settings For example, you can add rules about: * Preferred plotting libraries (matplotlib, plotly, altair) * Data handling practices * Code style conventions * Error handling preferences Example custom rules: `[](#__codelineno-0-1)Use plotly for interactive visualizations and matplotlib for static plots [](#__codelineno-0-2)Prefer polars over pandas for data manipulation due to better performance [](#__codelineno-0-3)Include docstrings for all functions using NumPy style [](#__codelineno-0-4)Use Type hints for all function parameters and return values [](#__codelineno-0-5)Handle errors with try/except blocks and provide informative error messages [](#__codelineno-0-6)Follow PEP 8 style guidelines [](#__codelineno-0-7)When working with data: [](#__codelineno-0-8)- Use altair, plotly for declarative visualizations [](#__codelineno-0-9)- Prefer polars over pandas [](#__codelineno-0-10)- Ensure proper error handling for data operations [](#__codelineno-0-11)For plotting: [](#__codelineno-0-12)- Use px.scatter for scatter plots [](#__codelineno-0-13)- Use px.line for time series [](#__codelineno-0-14)- Include proper axis labels and titles [](#__codelineno-0-15)- Set appropriate color schemes` ### Connecting to an LLM[¶](#connecting-to-an-llm "Permanent link") You can connect to an LLM through the notebook settings menu, or by manually editing your `marimo.toml` configuration file. Prefer going through the notebook settings. To locate your configuration file, run: At the top, the path to your `marimo.toml` file will be shown. Below we describe how to connect marimo to your AI provider. #### OpenAI[¶](#openai "Permanent link") 1. Install openai: `pip install openai` 2. Add the following to your `marimo.toml` (or configure in the UI settings in the editor): marimo.toml `[](#__codelineno-2-1)[ai.open_ai] [](#__codelineno-2-2)# Get your API key from https://platform.openai.com/account/api-keys [](#__codelineno-2-3)api_key = "sk-proj-..." [](#__codelineno-2-4)# Choose a model, we recommend "gpt-4-turbo" [](#__codelineno-2-5)model = "gpt-4-turbo" [](#__codelineno-2-6)# Available models: gpt-4-turbo-preview, gpt-4, gpt-3.5-turbo [](#__codelineno-2-7)# See https://platform.openai.com/docs/models for all available models [](#__codelineno-2-8)[](#__codelineno-2-9)# Change the base_url if you are using a different OpenAI-compatible API [](#__codelineno-2-10)base_url = "https://api.openai.com/v1"` #### Anthropic[¶](#anthropic "Permanent link") To use Anthropic with marimo: 1. Sign up for an account at [Anthropic](https://console.anthropic.com/) and grab your [Anthropic Key](https://console.anthropic.com/settings/keys). 2. Add the following to your `marimo.toml` (or configure in the UI settings in the editor): marimo.toml `[](#__codelineno-3-1)[ai.open_ai] [](#__codelineno-3-2)model = "claude-3-7-sonnet-20250219" [](#__codelineno-3-3)# or any model from https://docs.anthropic.com/en/docs/about-claude/models [](#__codelineno-3-4)[](#__codelineno-3-5)[ai.anthropic] [](#__codelineno-3-6)api_key = "sk-ant-..."` #### AWS Bedrock[¶](#aws-bedrock "Permanent link") AWS Bedrock provides access to foundation models from leading AI companies through a unified AWS API. To use AWS Bedrock with marimo: 1. Set up an [AWS account](https://aws.amazon.com/) with access to the AWS Bedrock service. 2. [Enable model access](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access.html) for the specific models you want to use in the AWS Bedrock console. 3. Install the boto3 Python client: `pip install boto3` 4. Configure AWS credentials using one of these methods: \* AWS CLI: Run `aws configure` to set up credentials \* Environment variables: Set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` \* AWS credentials file at `~/.aws/credentials` 5. Add the following to your `marimo.toml`: marimo.toml `[](#__codelineno-4-1)[ai.open_ai] [](#__codelineno-4-2)model = "bedrock/anthropic.claude-3-sonnet-20240229" [](#__codelineno-4-3)# Models are identified by bedrock/provider.model_name [](#__codelineno-4-4)# Examples: [](#__codelineno-4-5)# - bedrock/anthropic.claude-3-sonnet-20240229 [](#__codelineno-4-6)# - bedrock/meta.llama3-8b-instruct-v1:0 [](#__codelineno-4-7)# - bedrock/amazon.titan-text-express-v1 [](#__codelineno-4-8)# - bedrock/cohere.command-r-plus-v1 [](#__codelineno-4-9)[](#__codelineno-4-10)[ai.bedrock] [](#__codelineno-4-11)region_name = "us-east-1" # AWS region where Bedrock is available [](#__codelineno-4-12)# Optional AWS profile name (from ~/.aws/credentials) [](#__codelineno-4-13)profile_name = "my-profile"` If you're using an AWS named profile different from your default, specify the profile\_name. For explicit credentials (not recommended), you can use environment variables instead. #### Google AI[¶](#google-ai "Permanent link") To use Google AI with marimo: 1. Sign up for an account at [Google AI Studio](https://aistudio.google.com/app/apikey) and obtain your API key. 2. Install the Google AI Python client: `pip install google-genai` 3. Add the following to your `marimo.toml` (or configure in the UI settings in the editor): marimo.toml `[](#__codelineno-5-1)[ai.open_ai] [](#__codelineno-5-2)model = "gemini-2.5-flash-preview-05-20" [](#__codelineno-5-3)# or any model from https://ai.google.dev/gemini-api/docs/models/gemini [](#__codelineno-5-4)[](#__codelineno-5-5)[ai.google] [](#__codelineno-5-6)api_key = "AI..."` #### GitHub Copilot[¶](#github-copilot "Permanent link") You can use your GitHub Copilot for code refactoring or the chat panel. This requires a GitHub Copilot subscription. 1. Download the `gh` CLI from [here](https://cli.github.com/). 2. Create a token with `gh auth token` and copy the token. 3. Add the token to your `marimo.toml` (or configure in the UI settings in the editor). marimo.toml `[](#__codelineno-6-1)[ai.open_ai] [](#__codelineno-6-2)model = "gpt-4o-mini" [](#__codelineno-6-3)api_key = "gho_..." [](#__codelineno-6-4)base_url = "https://api.githubcopilot.com/"` #### Local models with Ollama[¶](#using-ollama "Permanent link") Ollama allows you to run open-source LLMs on your local machine. To integrate Ollama with marimo: 1. Download and install [Ollama](https://ollama.com/). 2. Download the model you want to use: `[](#__codelineno-7-1)# View available models at https://ollama.com/library [](#__codelineno-7-2)ollama pull llama3.1 [](#__codelineno-7-3)ollama pull codellama # recommended for code generation [](#__codelineno-7-4)[](#__codelineno-7-5)# View your installed models [](#__codelineno-7-6)ollama ls` 3. Start the Ollama server in a terminal: `[](#__codelineno-8-1)ollama serve [](#__codelineno-8-2)# In a new terminal [](#__codelineno-8-3)ollama run codellama # or any model from ollama ls` 4. Visit [http://127.0.0.1:11434](http://127.0.0.1:11434/) to confirm that the server is running. Port already in use If you get a "port already in use" error, you may need to close an existing Ollama instance. On Windows, click the up arrow in the taskbar, find the Ollama icon, and select "Quit". This is a known issue (see [Ollama Issue #3575](https://github.com/ollama/ollama/issues/3575)). Once you've closed the existing Ollama instance, you should be able to run `ollama serve` successfully. 5. Open a new terminal and install the openai client (e.g. `pip install openai`, `uv add openai`) 6. Start marimo: 7. Add the following to your `marimo.toml` (or configure in the UI settings in the editor): marimo.toml ``[](#__codelineno-10-1)[ai.open_ai] [](#__codelineno-10-2)api_key = "ollama" # This is not used, but required [](#__codelineno-10-3)model = "codellama" # or another model from `ollama ls` [](#__codelineno-10-4)base_url = "http://127.0.0.1:11434/v1"`` #### Other AI providers[¶](#other-ai-providers "Permanent link") marimo supports OpenAI's API by default. Many providers offer OpenAI API-compatible endpoints, which can be used by simply changing the `base_url` in your configuration. For example, providers like [GROQ](https://console.groq.com/docs/openai) and [DeepSeek](https://platform.deepseek.com/) follow this pattern. Using OpenAI-compatible providers (e.g., DeepSeek) Via marimo.tomlVia UI Settings Add the following configuration to your `marimo.toml` file: `[](#__codelineno-11-1)[ai.open_ai] [](#__codelineno-11-2)api_key = "dsk-..." # Your provider's API key [](#__codelineno-11-3)model = "deepseek-chat" # or "deepseek-reasoner" [](#__codelineno-11-4)base_url = "https://api.deepseek.com/"` 1. Open marimo's Settings panel 2. Navigate to the AI section 3. Enter your provider's API key in the "OpenAI API Key" field 4. Under AI Assist settings: - Set Base URL to your provider's endpoint (e.g., `https://api.deepseek.com`) - Set Model to your chosen model (e.g., `deepseek-chat` or `deepseek-reasoner`) For a comprehensive list of compatible providers and their configurations, please refer to the [liteLLM Providers documentation](https://litellm.vercel.app/docs/providers). For providers not compatible with OpenAI's API, please submit a [feature request](https://github.com/marimo-team/marimo/issues/new?template=feature_request.yaml) or "thumbs up" an existing one. Copilots[¶](#copilots "Permanent link") --------------------------------------- ### GitHub Copilot[¶](#github-copilot_1 "Permanent link") The marimo editor natively supports [GitHub Copilot](https://copilot.github.com/), an AI pair programmer, similar to VS Code: 1. Install [Node.js](https://nodejs.org/en/download). 2. Enable Copilot via the settings menu in the marimo editor. _GitHUb Copilot is not yet available in our conda distribution; please install marimo using `pip`/`uv` if you need Copilot._ ### Windsurf Copilot[¶](#windsurf-copilot "Permanent link") Windsurf (formerly codeium) provides a free coding copilot. You can try setting up Windsurf with the following: 1. Go to the Windsurf website and sign up for an account: [https://windsurf.com/](https://windsurf.com/) 2. Try the method from: [https://github.com/leona/helix-gpt/discussions/60](https://github.com/leona/helix-gpt/discussions/60) Add your key to your marimo.toml file (or configure in the UI settings in the editor): marimo.toml `[](#__codelineno-12-1)[completion] [](#__codelineno-12-2)copilot = "codeium" [](#__codelineno-12-3)codeium_api_key = ""` For official support, please ping the Windsurf team and ask them to support marimo. Alternative: Obtain Windsurf API key using VS Code 1. Go to the Codeium website and sign up for an account: [https://codeium.com/](https://codeium.com/) 2. Install the [Codeium Visual Studio Code extension](vscode:extension/codeium.codeium) (see [here](https://codeium.com/vscode_tutorial) for complete guide) 3. Sign in to your Codeium account in the VS Code extension 4. Select the Codeium icon on the Activity bar (left side), which opens the Codeium pane 5. Select the **Settings** button (gear icon) in the top-right corner of the Codeium pane Open Codeium settings 6. Click the **Download** link under the **Extension Diagnostics** section 7. Open the diagnostic file and search for `apiKey` Download diagnostics file with API key 8. Copy the value of the `apiKey` to `$XDG_CONFIG_HOME/marimo/marimo.toml`: marimo.toml `[](#__codelineno-13-1)[completion] [](#__codelineno-13-2)codeium_api_key = "a1e8..." # <-- paste your API key here [](#__codelineno-13-3)copilot = "codeium" [](#__codelineno-13-4)activate_on_typing = true` ### Custom copilots[¶](#custom-copilots "Permanent link") marimo also supports integrating with custom LLM providers for code completion suggestions. This allows you to use your own LLM service to provide in-line code suggestions based on internal providers or local models (e.g. Ollama). You may also use OpenAI, Anthropic, or Google AI by providing your own API keys. To configure a custom copilot: 1. Ensure you have an LLM provider that offers API access for code completion (either external or running locally) 2. Add the following configuration to your `marimo.toml` (or configure in the UI settings in the editor): marimo.toml `[](#__codelineno-14-1)[completion] [](#__codelineno-14-2)copilot = "custom" [](#__codelineno-14-3)api_key = "your-llm-api-key" [](#__codelineno-14-4)model = "your-llm-model-name" [](#__codelineno-14-5)base_url = "http://127.0.0.1:11434/v1" # or https://your-llm-api-endpoint.com` The configuration options include: * `api_key`: Your LLM provider's API key. This may not be required for local models, so you can set it to any random string. * `model`: The specific model to use for completion suggestions. * `base_url`: The endpoint URL for your LLM provider's API Package management - marimo https://docs.marimo.io/guides/editor_features/package_management/ marimo supports package management for `pip`, `uv`, `poetry`, `pixi`, and `rye`. When marimo comes across a module that is not installed, you will be prompted to install it using your preferred package manager. Once the module is installed, all cells that depend on the module will be rerun. Package Installation We use some heuristic for guessing the package name in your registry (e.g. PyPI) from the module name. It is possible that the package name is different from the module name. If you encounter an error, please file an issue or help us by adding your mapping [directly to the codebase](https://github.com/marimo-team/marimo/blob/main/marimo/_runtime/packages/module_name_to_pypi_name.py). Package reproducibility[¶](#package-reproducibility "Permanent link") --------------------------------------------------------------------- marimo is the only Python notebook that is reproducible down to the packages they use. This makes it possible to share standalone notebooks without shipping `requirements.txt` files alongside them, and guarantees your notebooks will work weeks, months, even years into the future. To learn more, see the [package reproducibility guide](https://docs.marimo.io/guides/package_management/inlining_dependencies/). Language Server - marimo https://docs.marimo.io/guides/editor_features/language_server/ Language Server Protocol (LSP)[¶](#language-server-protocol-lsp "Permanent link") --------------------------------------------------------------------------------- Experimental Feature LSP support in marimo is currently an experimental feature. It may have bugs or performance issues. To enable it add the following to your `pyproject.toml` file: pyproject.toml `[](#__codelineno-0-1)[tool.marimo.experimental] [](#__codelineno-0-2)lsp = true` The marimo editor supports the Language Server Protocol (LSP) to provide enhanced code intelligence features like: * Code completion * Hover information * Go to definition * Error checking and diagnostics Installation[¶](#installation "Permanent link") ----------------------------------------------- LSP support requires additional dependencies. You can install them with: `[](#__codelineno-1-1)pip install "marimo[lsp]" [](#__codelineno-1-2)# or [](#__codelineno-1-3)uv add "marimo[lsp]" [](#__codelineno-1-4)# or [](#__codelineno-1-5)conda install -c conda-forge python-lsp-server python-lsp-ruff` This will install the necessary packages including: * [`python-lsp-server`](https://github.com/python-lsp/python-lsp-server): The core Python language server * `python-lsp-ruff`: Ruff integration for fast linting You may optionally install other `pylsp` plugins. Other Python Language Servers Support for other Python language servers is planned for future releases. Configuration[¶](#configuration "Permanent link") ------------------------------------------------- LSP support can be configured in your `pyproject.toml` file. pyproject.toml `[](#__codelineno-2-1)[tool.marimo.experimental] [](#__codelineno-2-2)lsp = true` pyproject.toml `[](#__codelineno-3-1)# Language server configuration [](#__codelineno-3-2)[tool.marimo.language_servers.pylsp] [](#__codelineno-3-3)enabled = true # Enable/disable the Python language server [](#__codelineno-3-4)enable_mypy = true # Type checking with mypy (enabled by default, if installed) [](#__codelineno-3-5)enable_ruff = true # Linting with ruff (enabled by default, if installed) [](#__codelineno-3-6)enable_flake8 = false # Linting with flake8 [](#__codelineno-3-7)enable_pydocstyle = false # Check docstring style [](#__codelineno-3-8)enable_pylint = false # Linting with pylint [](#__codelineno-3-9)enable_pyflakes = false # Syntax checking with pyflakes [](#__codelineno-3-10)[](#__codelineno-3-11)# Diagnostics configuration [](#__codelineno-3-12)[tool.marimo.diagnostics] [](#__codelineno-3-13)enabled = true # Show diagnostics in the editor` WebAssembly[¶](#webassembly "Permanent link") --------------------------------------------- Language Servers are not available when running marimo in WebAssembly. Troubleshooting[¶](#troubleshooting "Permanent link") ----------------------------------------------------- If you encounter issues with the language server: 1. Make sure you've installed the required dependencies with `pip install "marimo[lsp]"` 2. Check if the language server is enabled in your configuration 3. Try restarting the marimo server 4. Check the terminal for any error messages Module autoreloading - marimo https://docs.marimo.io/guides/editor_features/module_autoreloading/ marimo has an advanced module autoreloader built-in, which you can enable in the [notebook settings](https://docs.marimo.io/guides/configuration/runtime_configuration/). When you make edits to Python modules that your notebook has imported, the module autoreloader will automatically mark cells that use them as stale and, optionally, automatically run them. Why autoreload? Autoreloading enables a workflow that many developers find productive: develop complex logic in Python modules, and use the marimo notebook as a DAG or main script that orchestrates your logic. Based on static analysis, the reloader only runs cells affected by your edits. The reloader is recursive, meaning that marimo tracks modifications for modules imported by your notebook's imported modules too. These two featuers make marimo's module autoreloader far more advanced than IPython's. Autoreloading comes in two types: 1. **autorun**: automatically re-runs cells affected by module modification. When set to autorun, marimo's reloader automatically run cells when you edit Python files. 2. **lazy**: marks cells affected by module modifications as stale, letting you know which cells need to be re-run. When set to lazy, marimo's reloader marks cells as stale when you edit Python files. Using your own editor - marimo https://docs.marimo.io/guides/editor_features/watching/ While we recommend using the [marimo editor](https://docs.marimo.io/guides/editor_features/), we understand that you may prefer to use your own. marimo provides a `--watch` flag that watches your notebook file for changes, syncing them to the marimo editor or running application. This lets you edit your notebook using an editor of your choice, like neovim, VSCode, Cursor, or PyCharm, and have the changes automatically reflected in your browser. Install watchdog for better file watching For better performance, install [watchdog](https://pypi.org/project/watchdog/). Without watchdog, marimo resorts to polling. marimo's file format[¶](#marimos-file-format "Permanent link") -------------------------------------------------------------- File format tutorial Run `marimo tutorial fileformat` at the command line for a full guide. marimo stores notebooks as Python files. Cells are stored as functions, decorated with`@app.cell`; you can optionally give cells names in the editor UI or by editing the notebook file. `[](#__codelineno-0-1)@app.cell [](#__codelineno-0-2)def memorable_cell_name(auto, determined, references): # signature denotes cell references [](#__codelineno-0-3) computed_value = auto + determined + references [](#__codelineno-0-4) "hello!" # final statement is the visual output [](#__codelineno-0-5) return computed_value # return denotes cell definitions` Cell signature and returns Don't worry about maintaining the signatures of cells and their return values; marimo will handle this for you. ### Exposing functions and classes top-level[¶](#exposing-functions-and-classes-top-level "Permanent link") You can expose top-level functions and classes in your notebook, so that other Python modules can import them: `[](#__codelineno-1-1)from my_notebook import my_function, MyClass` Top-level functions are added to a notebook using the `@app.function` decorator, and classes with `@app.class_definition`; these appear in your notebook as cells with just a function or class definition. These functions and classes must be pure, closing over only other pure functions and classes, or imports and constants defined in an `app.setup` `with` block. Here is a complete example that you can copy/paste and run locally: `[](#__codelineno-2-1)import marimo [](#__codelineno-2-2)[](#__codelineno-2-3)app = marimo.App() [](#__codelineno-2-4)[](#__codelineno-2-5)with app.setup: [](#__codelineno-2-6) # These symbols can be used by top-level functions and classes [](#__codelineno-2-7) # (as well as by regular cells) [](#__codelineno-2-8) import numpy as np [](#__codelineno-2-9) [](#__codelineno-2-10) CONSTANT: int = 1 [](#__codelineno-2-11)[](#__codelineno-2-12)@app.function [](#__codelineno-2-13)def my_function(x: np.ndarray): [](#__codelineno-2-14) return np.mean(x) + CONSTANT [](#__codelineno-2-15)[](#__codelineno-2-16)@app.class_definition [](#__codelineno-2-17)class MyClass: [](#__codelineno-2-18) ... [](#__codelineno-2-19)[](#__codelineno-2-20)@app.cell [](#__codelineno-2-21)def _(): [](#__codelineno-2-22) my_function(np.random.randn(2, 2)) [](#__codelineno-2-23) return [](#__codelineno-2-24)[](#__codelineno-2-25)if __name__ == "__main__": [](#__codelineno-2-26) app.run()` For more details see the [guide on reusable functions and classes](https://docs.marimo.io/guides/reusing_functions/). ### Types and autocompletion[¶](#types-and-autocompletion "Permanent link") Add type hints to your variables, and marimo will carry over these type hints to cells where these variables are used. This, combined with importing modules in the setup cell (see below for an example), makes it possible for your editor to give completions on the references of your cell. For example: `[](#__codelineno-3-1)# setup cell [](#__codelineno-3-2)import numpy as np [](#__codelineno-3-3)[](#__codelineno-3-4)# cell 1 [](#__codelineno-3-5)x: np.ndarray [](#__codelineno-3-6)[](#__codelineno-3-7)# cell 2 [](#__codelineno-3-8)np.mean(x)` will be serialized as `[](#__codelineno-4-1)import marimo [](#__codelineno-4-2)[](#__codelineno-4-3)app = marimo.App() [](#__codelineno-4-4)[](#__codelineno-4-5)with app.setup: [](#__codelineno-4-6) import numpy as np [](#__codelineno-4-7)[](#__codelineno-4-8)@app.cell [](#__codelineno-4-9)def _(): [](#__codelineno-4-10) x: np.ndarray [](#__codelineno-4-11) return x, [](#__codelineno-4-12)[](#__codelineno-4-13)@app.cell [](#__codelineno-4-14)def _(x: np.ndarray): [](#__codelineno-4-15) np.mean(x) [](#__codelineno-4-16)[](#__codelineno-4-17)if __name__ == "__main__": [](#__codelineno-4-18) app.run()` ### As markdown[¶](#as-markdown "Permanent link") Markdown File format tutorial Run `marimo tutorial markdown-format` at the command line for a full guide. marimo notebooks can also be stored as Markdown files. This is a good option for prose heavy text, and can be easy to navigate and edit in external editors. To convert a marimo notebook to markdown, use `[](#__codelineno-5-1)marimo export md notebook.py -o notebook.md` at the command-line, or rename your file to have an `.md` extension in the notebook editor. marimo conforms to standard markdown document format, and will render most places like Github. Metadata in this file format is saved in the frontmatter, which marimo may use for information like [sandboxing](https://docs.marimo.io/guides/package_management/inlining_dependencies/), and the marimo version. All other fields are kept, but ignored. For execution, marimo extracts code fences that contain `marimo` in braces. For instance `python {marimo}`, `{marimo}` or `{.marimo .python}`. The marimo editor uses `python {.marimo}` which is Pandoc compatible, and correctly processed by text highlighters. ` [](#__codelineno-6-1)--- [](#__codelineno-6-2)title: My Notebook [](#__codelineno-6-3)marimo-version: 0.0.0 [](#__codelineno-6-4)description: A notebook with a description [](#__codelineno-6-5)--- [](#__codelineno-6-6)[](#__codelineno-6-7)# Just a notebook [](#__codelineno-6-8)[](#__codelineno-6-9)```python {.marimo} [](#__codelineno-6-10)print("Hello World!") [](#__codelineno-6-11)``` ` marimo's markdown format can be used with a [`mkdocs plugin`](https://github.com/marimo-team/mkdocs-marimo) and [`Quarto`](https://github.com/marimo-team/quarto-marimo). Note that the markdown format is not as fully featured as the Python format. Reactive tests will not work, markdown notebooks cannot be imported or used as a library, and they cannot be run as scripts. Watching for changes to your notebook[¶](#watching-for-changes-to-your-notebook "Permanent link") ------------------------------------------------------------------------------------------------- ### `marimo edit --watch`[¶](#marimo-edit-watch "Permanent link") When you run `marimo edit` with the `--watch` flag, the marimo server will open your notebook in the browser and watch the underlying notebook file for changes. When you make changes to the notebook file, they will be streamed to the marimo editor in the browser. By default, synced code will not be executed automatically, with cells marked as stale instead. Run all stale cells with the marimo editor's "Run" button, or the [`runStale` hotkey](https://docs.marimo.io/guides/editor_features/hotkeys/), to see the new outputs. If you want to run all affected cells automatically when you save, change the `runtime` config in your `pyproject.toml` file. `[](#__codelineno-7-1)[tool.marimo.runtime] [](#__codelineno-7-2)watcher_on_save = "autorun"` ### `marimo run --watch`[¶](#marimo-run-watch "Permanent link") When you run `marimo run` with the `--watch` flag, whenever the file watcher detects a change to the notebook file, the application will be refreshed. The browser will trigger a page refresh to ensure your notebook starts from a fresh state. Watching for changes to other modules[¶](#watching-for-changes-to-other-modules "Permanent link") ------------------------------------------------------------------------------------------------- marimo can also watch for changes to Python modules that your notebook imports, letting you edit auxiliary Python files in your own editor as well. Learn how to enable this feature in our [Module Autoreloading Guide](https://docs.marimo.io/guides/editor_features/module_autoreloading/) Watching for data changes[¶](#watching-for-data-changes "Permanent link") ------------------------------------------------------------------------- Data file watching now supported! marimo now supports watching data files and automatically refreshing cells that depend on them using `mo.watch.file()` and `mo.watch.directory()`. Learn more in the [watch API documentation](https://docs.marimo.io/api/watch/). Hot-reloading WebAssembly notebooks[¶](#hot-reloading-webassembly-notebooks "Permanent link") --------------------------------------------------------------------------------------------- Follow these steps to develop a notebook using your own editor while previewing it as a [WebAssembly notebook](https://docs.marimo.io/guides/wasm/) in the browser. This lets you take advantage of local development tools while seeing the notebook as it appears when deployed as a WebAssembly notebook. `[](#__codelineno-8-1)# in one terminal, start a watched edit (or run) session [](#__codelineno-8-2)marimo edit notebook.py --watch [](#__codelineno-8-3)[](#__codelineno-8-4)# in another terminal [](#__codelineno-8-5)marimo export html-wasm notebook.py -o output_dir --watch [](#__codelineno-8-6)[](#__codelineno-8-7)# in a third terminal, serve the WASM application [](#__codelineno-8-8)cd path/to/output_dir [](#__codelineno-8-9)python -m http.server # or a server that watches for changes` Hotkeys - marimo https://docs.marimo.io/guides/editor_features/hotkeys/ If you'd like to override the default hotkeys, you can do so in the hotkeys menu (`Ctrl/Cmd-Shift-h`), or modifying your `marimo.toml`. You can find a list of available hotkeys below: | Hotkey | | --- | | `cell.aiCompletion` | | `cell.cellActions` | | `cell.complete` | | `cell.createAbove` | | `cell.createBelow` | | `cell.delete` | | `cell.findAndReplace` | | `cell.focusDown` | | `cell.focusUp` | | `cell.fold` | | `cell.foldAll` | | `cell.format` | | `cell.goToDefinition` | | `cell.hideCode` | | `cell.moveUp` | | `cell.moveDown` | | `cell.moveLeft` | | `cell.moveRight` | | `cell.redo` | | `cell.run` | | `cell.runAndNewAbove` | | `cell.runAndNewBelow` | | `cell.selectNextOccurrence` | | `cell.sendToBottom` | | `cell.sendToTop` | | `cell.splitCell` | | `cell.undo` | | `cell.unfold` | | `cell.unfoldAll` | | `cell.viewAsMarkdown` | | `completion.moveDown` | | `completion.moveUp` | | `global.commandPalette` | | `global.focusBottom` | | `global.focusTop` | | `global.foldCode` | | `global.formatAll` | | `global.hideCode` | | `global.interrupt` | | `global.runStale` | | `global.save` | | `global.showHelp` | | `global.toggleLanguage` | | `global.toggleTerminal` | | `global.toggleSidebar` | | `global.unfoldCode` | | `markdown.blockquote` | | `markdown.bold` | | `markdown.code` | | `markdown.italic` | | `markdown.link` | | `markdown.orderedList` | | `markdown.unorderedList` | Run notebooks as apps - marimo https://docs.marimo.io/guides/apps/ Run as an app[¶](#run-as-an-app "Permanent link") ------------------------------------------------- The marimo CLI lets you run any notebook as an app: `marimo run` lays out the notebook as an app and starts a web server that hosts the resulting app. By default, apps are laid out as a concatenation of their outputs, with code hidden. You can customize the layout using marimo's built-in drag-and-drop grid editor; you can also choose to include code in the app view. CLI[¶](#cli "Permanent link") ----------------------------- Run marimo notebooks as apps with View the [CLI documentation](https://docs.marimo.io/cli/#marimo-run) for more details. Layout[¶](#layout "Permanent link") ----------------------------------- While editing a notebook with `marimo edit`, you can preview the notebook as an app by clicking the preview button in the bottom-right of the editor. (You can also use the command palette.) ### Vertical layout[¶](#vertical-layout "Permanent link") The default layout is the vertical layout: cell outputs are concatenated vertically and code is hidden. When combined with marimo's [built-in functions for laying out outputs](https://docs.marimo.io/api/layouts/), as well as its configurable app widths (configure via the notebook settings menu), the vertical layout can successfully support a wide breadth of application user interfaces. ### Grid layout[¶](#grid-layout "Permanent link") If you prefer a drag-and-drop experience over [programmatic layout](https://docs.marimo.io/api/layouts/), consider using marimo's grid editor for making your apps: with this editor, you simply drag outputs onto a grid to arrange them on the page. Enable the grid editor in the app preview, via a dropdown: marimo saves metadata about your constructed layout in a `layouts` folder; make sure to include this folder when sharing your notebook so that others can reconstruct your layout. ### Slides layout[¶](#slides-layout "Permanent link") If you prefer a slideshow-like experience, you can use the slides layout. Enable the slides layout in the app preview, via the same dropdown as above. Unlike the grid layout, the slides are much less customizable: * The order of the slides is determined by the order of the cells in the notebook. * The slides do not support drag-and-drop rearrangement or resizing. * All outputs are shown and all code is hidden. If you need more control over the layout, please file an issue on [GitHub](https://github.com/marimo-team/marimo/issues), so we can properly prioritize this feature. Testing notebooks - marimo https://docs.marimo.io/guides/testing/ Because marimo notebooks are stored as Python, test them like any other Python program. | Guide | Description | | --- | --- | | [pytest](https://docs.marimo.io/guides/testing/pytest/) | Include unit tests in notebooks, or implement entire tests as notebooks | | [doctest](https://docs.marimo.io/guides/testing/doctest/) | Test code snippets in docstrings using doctest | Run notebooks as scripts - marimo https://docs.marimo.io/guides/scripts/ Run as a script[¶](#run-as-a-script "Permanent link") ----------------------------------------------------- You can run marimo notebooks as scripts at the command line, just like any other Python script. For example, `[](#__codelineno-0-1)python my_marimo_notebook.py` Running a notebook as a script is useful when your notebook has side-effects, like writing to disk. Print statements and other console outputs will show up in your terminal. Saving notebook outputs To run as a script while also saving HTML of the notebook outputs, use `[](#__codelineno-1-1)marimo export html notebook.py -o notebook.html` You can also pass command-line arguments to your notebook during export. Separate these args from the command with two dashes: `[](#__codelineno-2-1)marimo export html notebook.py -o notebook.html -- -arg value` Exporting to other formats, such as ipynb, is also possible: `[](#__codelineno-3-1)marimo export ipynb notebook.py -o notebook.ipynb -- -arg value` Command-line arguments[¶](#command-line-arguments "Permanent link") ------------------------------------------------------------------- When run as a script, you can access your notebook's command-line arguments through `sys.argv`, just like any other Python program. This also means you can declare your notebook's command-line arguments using Python libraries like [`argparse`](https://docs.python.org/3/library/argparse.html) and [`simple-parsing`](https://github.com/lebrice/SimpleParsing). These examples shows how to conditionally assign values to variables based on command-line arguments when running as a script, and use default values when running as a notebook. ### argparse[¶](#argparse "Permanent link") Source code for `examples/running_as_a_script/sharing_arguments.py` Tip: paste this code into an empty cell, and the marimo editor will create cells for you ``import marimo __generated_with = "0.11.31" app = marimo.App(width="medium") @app.cell def _(): import marimo as mo return (mo,) @app.cell def _(): import argparse return (argparse,) @app.cell(hide_code=True) def _(mo): mo.md( """ This notebook shows how to parametrize a notebook with optional command-line arguments. Run the notebook with ```bash marimo edit sharing_arguments.py ``` or ```bash marimo edit sharing_arguments.py -- -learning_rate=1e-3 ``` (Note the `--` separating the filename from the arguments.) or ```bash python sharing_arguments.py -learning_rate=1e-3 ``` See help for the notebook's arguments with ```python python sharing_arguments.py --help ``` """ ) return @app.cell def _(mo): default = mo.ui.number(1000, step=100) default return (default,) @app.cell def _(argparse, default): parser = argparse.ArgumentParser() parser.add_argument("-iterations", default=default.value) args = parser.parse_args() print(args.iterations) return args, parser if __name__ == "__main__": app.run()`` ### simple-parsing[¶](#simple-parsing "Permanent link") Source code for `examples/running_as_a_script/with_simple_parsing.py` Tip: paste this code into an empty cell, and the marimo editor will create cells for you `# /// script # requires-python = ">=3.12" # dependencies = [ # "marimo", # "simple-parsing==0.1.7", # ] # /// import marimo __generated_with = "0.11.31" app = marimo.App(width="medium") @app.cell def _(): import marimo as mo return (mo,) @app.cell def _(): import simple_parsing return (simple_parsing,) @app.cell def _(): from dataclasses import dataclass from simple_parsing import ArgumentParser parser = ArgumentParser() parser.add_argument("--foo", type=int, default=123, help="foo help") @dataclass class Options: """Help string for this group of command-line arguments.""" log_dir: str # Help string for a required str argument learning_rate: float = 1e-4 # Help string for a float argument parser.add_arguments(Options, dest="options") return ArgumentParser, Options, dataclass, parser @app.cell def _(Options, mo, parser): from dataclasses import fields def parse_args(): if mo.running_in_notebook(): # set default values for the command-line arguments when running as a notebook return "foo default", Options("logs/", 1e-4) else: args = parser.parse_args() return args.foo, args.options return fields, parse_args @app.cell def _(parse_args): foo, options = parse_args() print(foo, options) return foo, options if __name__ == "__main__": app.run()` Reuse functions and classes - marimo https://docs.marimo.io/guides/reusing_functions/ Importing functions and classes defined in notebooks[¶](#importing-functions-and-classes-defined-in-notebooks "Permanent link") ------------------------------------------------------------------------------------------------------------------------------- You can import top-level functions and classes defined in a marimo notebook into other Python scripts or notebooks using normal Python syntax, as long as your definitions satisfy the simple criteria described on this page. This makes your notebook code reusable, testable, and easier to edit in text editors of your choice. Overview[¶](#overview "Permanent link") --------------------------------------- For a function or class to be saved at the top level of the notebook file, it must meet the following **criteria**: 1. The cell must define just a single function or class. 2. The defined function or class can only refer to symbols defined in the [setup cell](#1-create-a-setup-cell), or to other top-level symbols. ### Example[¶](#example "Permanent link") In another script or notebook `[](#__codelineno-1-1)from my_notebook import my_utility_function, DataProcessor` Creating a top-level function or class[¶](#creating-a-top-level-function-or-class "Permanent link") --------------------------------------------------------------------------------------------------- ### 1\. Create a setup cell[¶](#1-create-a-setup-cell "Permanent link") First, add a **setup cell** to your notebook for imports that your functions or classes will need: To add a setup cell in the editor, open the notebook menu and select "Add setup cell". (The setup cell is guaranteed to run before other cells.) ### 2\. Define your function[¶](#2-define-your-function "Permanent link") Define a single function in a cell. If the [criteria](#overview) for top-level functions are met, a marker in the bottom right will indicate that it is a reusable function. Note Functions can only reference symbols defined in the setup cell. If a function cannot be serialized top-level, the marker in the bottom right will provide a description why. Under the hood, marimo decorates top-level functions with `@app.function`, which you can use to define your own top-level functions if you are editing a notebook file directly. Top-level classes are decorated with `@app.class_definition`. ### 3\. Import into other Python files[¶](#3-import-into-other-python-files "Permanent link") Now you can import your function in other notebooks or Python scripts: `[](#__codelineno-4-1)# In another_script.py [](#__codelineno-4-2)from my_notebook import calculate_statistics [](#__codelineno-4-3)[](#__codelineno-4-4)data = [1, 2, 3, 4, 5] [](#__codelineno-4-5)stats = calculate_statistics(data) [](#__codelineno-4-6)print(stats)` Best practices[¶](#best-practices "Permanent link") --------------------------------------------------- * Use setup cells for widely used imports * Keep function dependencies limited to setup-cell references, or other top-level declarations * Use descriptive names for your functions * Add docstrings to document your functions' behavior Tip Top-level symbols can reference other top-level symbols. Limitations[¶](#limitations "Permanent link") --------------------------------------------- * Functions cannot depend on variables defined in regular cells * Like other cells, cyclic dependencies between functions are not allowed * Functions cannot be exported from notebooks in [marimo's markdown format](https://docs.marimo.io/guides/editor_features/watching/#as-markdown). Learn more[¶](#learn-more "Permanent link") ------------------------------------------- For more on marimo's file format, check out our [documentation on using your own editor](https://docs.marimo.io/guides/editor_features/watching/) or view our [file format tutorial](https://links.marimo.app/tutorial-fileformat). Export to other formats - marimo https://docs.marimo.io/guides/exporting/ Exporting to HTML and other formats[¶](#exporting-to-html-and-other-formats "Permanent link") --------------------------------------------------------------------------------------------- Export marimo notebooks to other file formats at the command line using Export to static HTML[¶](#export-to-static-html "Permanent link") ----------------------------------------------------------------- ### Export from a running notebook[¶](#export-from-a-running-notebook "Permanent link") Export the current view your notebook to static HTML via the notebook menu: Download as static HTML. Additionally, you can configure individual notebooks to automatically save as HTML through the notebook menu. These automatic snapshots are saved to a folder called `__marimo__` in the notebook directory. ### Export from the command line[¶](#export-from-the-command-line "Permanent link") Export to HTML at the command line: `[](#__codelineno-1-1)marimo export html notebook.py -o notebook.html` or watch the notebook for changes and automatically export to HTML: `[](#__codelineno-2-1)marimo export html notebook.py -o notebook.html --watch` When you export from the command line, marimo runs your notebook to produce its visual outputs before saving as HTML. Note If any cells error during the export process, the status code will be non-zero. However, the export result may still be generated, with the error included in the output. Errors can be ignored by appending `|| true` to the command, e.g. `marimo export html notebook.py || true`. ### Pre-render HTML exports[¶](#pre-render-html-exports "Permanent link") Static marimo exports execute Javascript to render the notebook source code as HTML at browser runtime. If you would like to directly serve the HTML representation of your notebook, you can run the following post-processing script and serve the resulting file instead. `[](#__codelineno-3-1)# /// script [](#__codelineno-3-2)# requires-python = ">=3.9" [](#__codelineno-3-3)# dependencies = [ [](#__codelineno-3-4)# "playwright", [](#__codelineno-3-5)# ] [](#__codelineno-3-6)# /// [](#__codelineno-3-7)[](#__codelineno-3-8)import os [](#__codelineno-3-9)import subprocess [](#__codelineno-3-10)from playwright.sync_api import sync_playwright [](#__codelineno-3-11)[](#__codelineno-3-12)input_file = "input.html" [](#__codelineno-3-13)output_file = "output.html" [](#__codelineno-3-14)[](#__codelineno-3-15)subprocess.run(["playwright", "install", "chromium-headless-shell"], check=True) [](#__codelineno-3-16)[](#__codelineno-3-17)with sync_playwright() as p: [](#__codelineno-3-18) with p.chromium.launch(headless=True) as browser: [](#__codelineno-3-19) page = browser.new_page() [](#__codelineno-3-20) page.goto( [](#__codelineno-3-21) f"file:///{os.path.abspath(input_file)}", [](#__codelineno-3-22) wait_until="networkidle", [](#__codelineno-3-23) ) [](#__codelineno-3-24) with open(output_file, "w", encoding="utf-8") as f: [](#__codelineno-3-25) f.write(page.content())` Export to a Python script[¶](#export-to-a-python-script "Permanent link") ------------------------------------------------------------------------- Export to a flat Python script in topological order, so the cells adhere to their dependency graph. `[](#__codelineno-4-1)marimo export script notebook.py -o notebook.script.py` Top-level await not supported Exporting to a flat Python script does not support top-level await. If you have top-level await in your notebook, you can still execute the notebook as a script with `python notebook.py`. Export to markdown[¶](#export-to-markdown "Permanent link") ----------------------------------------------------------- Export to markdown notebook in top to bottom order, so the cells are in the order as they appear in the notebook. `[](#__codelineno-5-1)marimo export md notebook.py -o notebook.md` This can be useful to plug into other tools that read markdown, such as [Quarto](https://quarto.org/) or [MyST](https://myst-parser.readthedocs.io/). marimo can directly open markdown files as notebooks Learn more with `marimo tutorial markdown-format` at the command line. You can also convert the markdown back to a marimo notebook: `[](#__codelineno-6-1)marimo convert notebook.md > notebook.py` Export to Jupyter notebook[¶](#export-to-jupyter-notebook "Permanent link") --------------------------------------------------------------------------- Export to Jupyter notebook in topological order, so the cells adhere to their dependency graph. `[](#__codelineno-7-1)marimo export ipynb notebook.py -o notebook.ipynb` Exporting to PDF, slides, or rst[¶](#exporting-to-pdf-slides-or-rst "Permanent link") ------------------------------------------------------------------------------------- The marimo [Quarto](https://www.github.com/marimo-team/quarto-marimo) plugin enables exporting to PDF and other formats with Pandoc. See this [publishing](https://docs.marimo.io/guides/publishing/quarto/) for more details. However, if you export to a Jupyter notebook, you can leverage various other Jupyter ecosystem tools. For PDFs, you will need to have [Pandoc](https://nbconvert.readthedocs.io/en/latest/install.html#installing-pandoc) and [Tex](https://nbconvert.readthedocs.io/en/latest/install.html#installing-tex) installed. The examples below use `uvx`, which you can obtain by [installing `uv`](https://docs.astral.sh/uv/getting-started/installation/). `[](#__codelineno-8-1)NOTEBOOK=notebook.ipynb [](#__codelineno-8-2)[](#__codelineno-8-3)# Convert to PDF using nbconvert [](#__codelineno-8-4)uvx --with nbconvert --from jupyter-core jupyter nbconvert --to pdf $NOTEBOOK [](#__codelineno-8-5)[](#__codelineno-8-6)# Convert to web PDF [](#__codelineno-8-7)uvx --with "nbconvert[webpdf]" --from jupyter-core jupyter nbconvert --to webpdf $NOTEBOOK --allow-chromium-download [](#__codelineno-8-8)[](#__codelineno-8-9)# Convert to slides [](#__codelineno-8-10)uvx --with nbconvert --from jupyter-core jupyter nbconvert --to slides $NOTEBOOK [](#__codelineno-8-11)[](#__codelineno-8-12)# Convert to rst with nbconvert [](#__codelineno-8-13)uvx --with nbconvert --from jupyter-core jupyter nbconvert --to rst $NOTEBOOK [](#__codelineno-8-14)[](#__codelineno-8-15)# Generate PNG/PDF of specific cells using nbconvert [](#__codelineno-8-16)uvx --with nbconvert --with jupyter --from jupyter-core jupyter nbconvert --to pdf --execute --stdout $NOTEBOOK \ [](#__codelineno-8-17) --TemplateExporter.exclude_input=True [](#__codelineno-8-18)[](#__codelineno-8-19)# Use nbconvert programmatically for more control [](#__codelineno-8-20)uv run --with nbconvert python -c " [](#__codelineno-8-21)from nbconvert import PDFExporter [](#__codelineno-8-22)import nbformat [](#__codelineno-8-23)nb = nbformat.read('$NOTEBOOK', as_version=4) [](#__codelineno-8-24)pdf_exporter = PDFExporter() [](#__codelineno-8-25)pdf_data, resources = pdf_exporter.from_notebook_node(nb) [](#__codelineno-8-26)with open('notebook.pdf', 'wb') as f: [](#__codelineno-8-27) f.write(pdf_data) [](#__codelineno-8-28)"` You can also use other tools that work with Jupyter notebooks: * [Quarto](https://quarto.org/) - Create beautiful documents, websites, presentations * [nbgrader](https://nbgrader.readthedocs.io/) - Grade notebook assignments Export to WASM-powered HTML[¶](#export-to-wasm-powered-html "Permanent link") ----------------------------------------------------------------------------- Export your notebook to a self-contained HTML file that runs using WebAssembly: `[](#__codelineno-9-1)# export as readonly, with code locked [](#__codelineno-9-2)marimo export html-wasm notebook.py -o output_dir --mode run [](#__codelineno-9-3)# export as an editable notebook [](#__codelineno-9-4)marimo export html-wasm notebook.py -o output_dir --mode edit` The exported HTML file will run your notebook using WebAssembly, making it completely self-contained and executable in the browser. This means users can interact with your notebook without needing Python or marimo installed. Options: * `--mode`: Choose between `run` (read-only) or `edit` (allows editing) * `--output`: Directory to save the HTML and required assets * `--show-code/--no-show-code`: Whether to initially show or hide the code in the notebook * `--watch/--no-watch`: Watch the notebook for changes and automatically export * `--include-cloudflare`: Write configuration files necessary for deploying to Cloudflare Note The exported file must be served over HTTP to function correctly - it cannot be opened directly from the filesystem (`file://`). Your server must also serve the assets in the `assets` directory, next to the HTML file. For a simpler publishing experience, publish to [GitHub Pages](https://docs.marimo.io/guides/publishing/github_pages/), [Cloudflare](https://docs.marimo.io/guides/publishing/cloudflare/) or use the [online playground](https://docs.marimo.io/guides/publishing/playground/). Deploying to Cloudflare You can include `--include-cloudflare` for deploying to Cloudflare. For example: `[](#__codelineno-10-1)marimo export html-wasm notebook.py -o my_app/dist --include-cloudflare` To run locally, run: To deploy to Cloudflare, run: ### Testing the export[¶](#testing-the-export "Permanent link") You can test the export by running the following command in the directory containing your notebook: `[](#__codelineno-13-1)cd path/to/output_dir [](#__codelineno-13-2)python -m http.server` ### Including data files[¶](#including-data-files "Permanent link") See the docs for [mo.notebook\_location](https://docs.marimo.io/api/miscellaneous/#marimo.notebook_location " marimo.notebook_location") to learn how to include data files in exported WASM HTML notebooks. ### Publishing to GitHub Pages[¶](#publishing-to-github-pages "Permanent link") After exporting your notebook to WASM HTML, you can publish it to [GitHub Pages](https://pages.github.com/) for free. See our [guide on GitHub Pages](https://docs.marimo.io/guides/publishing/github_pages/) to learn more. ### Exporting multiple notebooks[¶](#exporting-multiple-notebooks "Permanent link") In order to export multiple notebooks under the same folder, you can use the following snippet: `[](#__codelineno-14-1)files=("batch_and_form.py" "data_explorer.py") [](#__codelineno-14-2)[](#__codelineno-14-3)for file in "${files[@]}"; do [](#__codelineno-14-4) without_extension="${file%.*}" [](#__codelineno-14-5) marimo export html-wasm "$file" -o site/"$without_extension".html --mode run [](#__codelineno-14-6)done` Optionally, you can create an `index.html` file in the public directory: `[](#__codelineno-15-1)echo "" >> site/index.html` 🏝️ Embed marimo outputs in HTML using Islands[¶](#embed-marimo-outputs-in-html-using-islands "Permanent link") --------------------------------------------------------------------------------------------------------------- Preview Islands are an early feature. While the API likely won't change, there are some improvements we'd like to make before we consider them stable. Please let us know on [GitHub](https://github.com/marimo-team/marimo/issues) if you run into any issues or have any feedback! marimo islands are a way to embed marimo outputs and/or python code in your HTML that will become interactive when the page is loaded. This is useful for creating interactive blog posts, tutorials, and educational materials, all powered by marimo's reactive runtime. Check out an [example island-powered document](https://docs.marimo.io/guides/island_example/). ### Generating islands[¶](#generating-islands "Permanent link") Use `MarimoIslandGenerator` to generate HTML for islands Example From code blocksFrom notebook files `[](#__codelineno-16-1)import asyncio [](#__codelineno-16-2)import sys [](#__codelineno-16-3)from marimo import MarimoIslandGenerator [](#__codelineno-16-4)[](#__codelineno-16-5)if sys.platform == 'win32': [](#__codelineno-16-6) asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) [](#__codelineno-16-7)[](#__codelineno-16-8)async def main(): [](#__codelineno-16-9) generator = MarimoIslandGenerator() [](#__codelineno-16-10) block1 = generator.add_code("import marimo as mo") [](#__codelineno-16-11) block2 = generator.add_code("mo.md('Hello, islands!')") [](#__codelineno-16-12) [](#__codelineno-16-13) # Build the app [](#__codelineno-16-14) app = await generator.build() [](#__codelineno-16-15) [](#__codelineno-16-16) # Render the app [](#__codelineno-16-17) output = f""" [](#__codelineno-16-18) [](#__codelineno-16-19) [](#__codelineno-16-20) {generator.render_head()} [](#__codelineno-16-21) [](#__codelineno-16-22) [](#__codelineno-16-23) {block1.render(display_output=False)} [](#__codelineno-16-24) {block2.render()} [](#__codelineno-16-25) [](#__codelineno-16-26) [](#__codelineno-16-27) """ [](#__codelineno-16-28) print(output) [](#__codelineno-16-29) # Save the HTML to a file [](#__codelineno-16-30) output_file = "output.html" [](#__codelineno-16-31) with open(output_file, "w", encoding="utf-8") as f: [](#__codelineno-16-32) f.write(output) [](#__codelineno-16-33)[](#__codelineno-16-34)if __name__ == '__main__': [](#__codelineno-16-35) asyncio.run(main())` `[](#__codelineno-17-1)from marimo import MarimoIslandGenerator [](#__codelineno-17-2)[](#__codelineno-17-3)# Create the generator from file [](#__codelineno-17-4)generator = MarimoIslandGenerator.from_file("./.py", display_code=False) [](#__codelineno-17-5)[](#__codelineno-17-6)# Generate and print the HTML without building [](#__codelineno-17-7)# This will still work for basic rendering, though without running the cells [](#__codelineno-17-8)html = generator.render_html(include_init_island=False) [](#__codelineno-17-9)print(html) [](#__codelineno-17-10)# Save the HTML to a file [](#__codelineno-17-11)output_file = "output.html" [](#__codelineno-17-12)with open(output_file, "w", encoding="utf-8") as f: [](#__codelineno-17-13) f.write(html)` Any relevant `.html` that gets generated can be run through the [`development.md`](https://github.com/marimo-team/marimo/blob/main/frontend/islands/development.md) file instructions. ### Islands in action[¶](#islands-in-action "Permanent link") Advanced topic! Islands are an advanced concept that is meant to be a building block for creating integrations with existing tools such as static site generators or documentation tools. In order to use marimo islands, you need to import the necessary JS/CSS headers in your HTML file, and use our custom HTML tags to define the islands. `[](#__codelineno-18-1) [](#__codelineno-18-2) [](#__codelineno-18-10) [](#__codelineno-18-11) [](#__codelineno-18-12) [](#__codelineno-18-16) [](#__codelineno-18-22) [](#__codelineno-18-23)[](#__codelineno-18-24) [](#__codelineno-18-25) [](#__codelineno-18-26) [](#__codelineno-18-27) [](#__codelineno-18-28) Hello, islands! [](#__codelineno-18-29) [](#__codelineno-18-30) [](#__codelineno-18-31) [](#__codelineno-18-32) [](#__codelineno-18-33)` marimo.MarimoIslandGenerator [¶](#marimo.MarimoIslandGenerator "Permanent link") -------------------------------------------------------------------------------- `[](#__codelineno-0-1)MarimoIslandGenerator(app_id: [str](https://docs.python.org/3/library/stdtypes.html#str) = 'main')` Generates Marimo islands for embedding in other pages. This is a great way to use another SSG framework that converts Python code to HTML using marimo-islands. Generally you will want to: 1. Find all the code snippets and add them to the generator. 2. Build the app. 3. Replace all code snippets with the rendered HTML. 4. Include the header in the tag. Examples: Using the MarimoIslandGenerator class: `[](#__codelineno-0-1)import asyncio [](#__codelineno-0-2)import sys [](#__codelineno-0-3)from marimo import MarimoIslandGenerator [](#__codelineno-0-4)[](#__codelineno-0-5)async def main(): [](#__codelineno-0-6) generator = MarimoIslandGenerator() [](#__codelineno-0-7) block1 = generator.add_code("import marimo as mo") [](#__codelineno-0-8) block2 = generator.add_code("mo.md('Hello, islands!')") [](#__codelineno-0-9) [](#__codelineno-0-10) # Build the app [](#__codelineno-0-11) app = await generator.build() [](#__codelineno-0-12) [](#__codelineno-0-13) # Render the app [](#__codelineno-0-14) output = f""" [](#__codelineno-0-15) [](#__codelineno-0-16) [](#__codelineno-0-17) {generator.render_head()} [](#__codelineno-0-18) [](#__codelineno-0-19) [](#__codelineno-0-20) {block1.render(display_output=False)} [](#__codelineno-0-21) {block2.render()} [](#__codelineno-0-22) [](#__codelineno-0-23) [](#__codelineno-0-24) """ [](#__codelineno-0-25) print(output) [](#__codelineno-0-26) # Save the HTML to a file [](#__codelineno-0-27) output_file = "output.html" [](#__codelineno-0-28) with open(output_file, "w", encoding="utf-8") as f: [](#__codelineno-0-29) f.write(output) [](#__codelineno-0-30)[](#__codelineno-0-31)if __name__ == '__main__': [](#__codelineno-0-32) asyncio.run(main())` You can also create the generator from a file: `[](#__codelineno-1-1)from marimo import MarimoIslandGenerator [](#__codelineno-1-2)[](#__codelineno-1-3)# Create the generator from file [](#__codelineno-1-4)generator = MarimoIslandGenerator.from_file( [](#__codelineno-1-5) "./.py", display_code=False [](#__codelineno-1-6)) [](#__codelineno-1-7)[](#__codelineno-1-8)# Generate and print the HTML without building [](#__codelineno-1-9)# This will still work for basic rendering, though without running the cells [](#__codelineno-1-10)html = generator.render_html(include_init_island=False) [](#__codelineno-1-11)print(html) [](#__codelineno-1-12)# Save the HTML to a file [](#__codelineno-1-13)output_file = "output.html" [](#__codelineno-1-14)with open(output_file, "w", encoding="utf-8") as f: [](#__codelineno-1-15) f.write(html)` | PARAMETER | DESCRIPTION | | --- | --- | | `app_id` | The optional identifier of the app, defaults to `main`. **TYPE:** `[str](https://docs.python.org/3/library/stdtypes.html#str)` **DEFAULT:** `'main'` | ### has\_run `instance-attribute` [¶](#marimo.MarimoIslandGenerator.has_run "Permanent link") ### add\_code [¶](#marimo.MarimoIslandGenerator.add_code "Permanent link") `[](#__codelineno-0-1)add_code(code: [str](https://docs.python.org/3/library/stdtypes.html#str), display_code: [bool](https://docs.python.org/3/library/functions.html#bool) = False, display_output: [bool](https://docs.python.org/3/library/functions.html#bool) = True, is_reactive: [bool](https://docs.python.org/3/library/functions.html#bool) = True, is_raw: [bool](https://docs.python.org/3/library/functions.html#bool) = False) -> MarimoIslandStub` Add a code cell to the app. _Args:_ * code (str): The code to add to the app. * display\_code (bool): Whether to display the code in the HTML. * display\_output (bool): Whether to display the output in the HTML. * is\_raw (bool): Whether to handled the code without formatting. * is\_reactive (bool): Whether this code block will run with pyodide. ### build `async` [¶](#marimo.MarimoIslandGenerator.build "Permanent link") Build the app. This should be called after adding all the code cells. _Returns:_ * App: The built app. ### from\_file `staticmethod` [¶](#marimo.MarimoIslandGenerator.from_file "Permanent link") `[](#__codelineno-0-1)from_file(filename: [str](https://docs.python.org/3/library/stdtypes.html#str), display_code: [bool](https://docs.python.org/3/library/functions.html#bool) = False) -> [MarimoIslandGenerator](#marimo.MarimoIslandGenerator " marimo.MarimoIslandGenerator (marimo._islands._island_generator.MarimoIslandGenerator)")` Create a MarimoIslandGenerator and populate MarimoIslandStubs using code cells from a marimo \*.py file. _Args:_ * filename (str): Marimo .py filename to convert to reactive HTML. * display\_code (bool): Whether to display the code in HTML snippets. ### render\_body [¶](#marimo.MarimoIslandGenerator.render_body "Permanent link") Render the body for the app. This should be included in the tag of the page. _Args:_ - include\_init\_island (bool): If True, adds initialization loader. - max\_width (str): CSS style max\_width property. - margin (str): CSS style margin property. - style (str): CSS style. Overrides max\_width and margin. ### render\_head [¶](#marimo.MarimoIslandGenerator.render_head "Permanent link") `[](#__codelineno-0-1)render_head(*, version_override: [str](https://docs.python.org/3/library/stdtypes.html#str) = __version__, _development_url: [Union](https://docs.python.org/3/library/typing.html#typing.Union "typing.Union")[[str](https://docs.python.org/3/library/stdtypes.html#str), [bool](https://docs.python.org/3/library/functions.html#bool)] = False) -> [str](https://docs.python.org/3/library/stdtypes.html#str)` Render the header for the app. This should be included in the tag of the page. _Args:_ * version\_override (str): Marimo version to use for loaded js/css. * \_development\_url (str): If True, uses local marimo islands js. ### render\_html [¶](#marimo.MarimoIslandGenerator.render_html "Permanent link") Render reactive html for the app. _Args:_ * version\_override (str): Marimo version to use for loaded js/css. * \_development\_url (str): If True, uses local marimo islands js. * include\_init\_island (bool): If True, adds initialization loader. * max\_width (str): CSS style max\_width property. * margin (str): CSS style margin property. * style (str): CSS style. Overrides max\_width and margin. ### render\_init\_island [¶](#marimo.MarimoIslandGenerator.render_init_island "Permanent link") `[](#__codelineno-0-1)render_init_island() -> [str](https://docs.python.org/3/library/stdtypes.html#str)` Renders a static html MarimoIsland str which displays a spinning initialization loader while Pyodide loads and disappears once the kernel is ready to use. pytest - marimo https://docs.marimo.io/guides/testing/pytest/ Testing with pytest[¶](#testing-with-pytest "Permanent link") ------------------------------------------------------------- Testing in notebook[¶](#testing-in-notebook "Permanent link") ------------------------------------------------------------- By default, marimo discovers and executes tests inside your notebook. When the optional `pytest` dependency is present, marimo runs `pytest` on cells that consist exclusively of test code - i.e. functions whose names start with `test_` or classes whose names start with `Test`. If a cell mixes in anything else (helper functions, constants, variables, imports, etc.), that cell is skipped by the test runner (we recommend you move helpers to another cell). For example, Reactive tests can be disabled You can disable this behavior with the `runtime.reactive_test` option in the configuration file. Testing at the command-line[¶](#testing-at-the-command-line "Permanent link") ----------------------------------------------------------------------------- Since marimo notebooks are Python programs, you can test them using [`pytest`](https://docs.pytest.org/en/stable/), a popular testing framework for Python. For example, runs and tests all notebook cells whose names start with `test_`, or cells that contain only `test_` functions and `Test` classes (just like in notebook tests). Naming cells Name a cell by giving its function a name in the notebook file, or using the cell action menu in the notebook editor. Use marimo notebooks just like normal pytest tests Include test notebooks (notebooks whose names start with `test_`) in your standard test suite, and `pytest` will discover them automatically. In addition, you can write self-contained notebooks that contain their own unit tests, and run `pytest` on them directly (`pytest my_notebook.py`). Example[¶](#example "Permanent link") ------------------------------------- Running `pytest` on `[](#__codelineno-2-1)# content of test_notebook.py [](#__codelineno-2-2)import marimo [](#__codelineno-2-3)[](#__codelineno-2-4)__generated_with = "0.10.6" [](#__codelineno-2-5)app = marimo.App() [](#__codelineno-2-6) [](#__codelineno-2-7)[](#__codelineno-2-8)@app.cell [](#__codelineno-2-9)def _(): [](#__codelineno-2-10) def inc(x): [](#__codelineno-2-11) return x + 1 [](#__codelineno-2-12) return (inc,) [](#__codelineno-2-13) [](#__codelineno-2-14)[](#__codelineno-2-15)@app.cell [](#__codelineno-2-16)def test_fails(inc): [](#__codelineno-2-17) assert inc(3) == 5, "This test fails" [](#__codelineno-2-18) [](#__codelineno-2-19)[](#__codelineno-2-20)@app.cell [](#__codelineno-2-21)def test_sanity(inc): [](#__codelineno-2-22) assert inc(3) == 4, "This test passes" [](#__codelineno-2-23)[](#__codelineno-2-24)@app.cell [](#__codelineno-2-25)def collection_of_tests(inc, pytest): [](#__codelineno-2-26) @pytest.mark.parametrize(("x", "y"), [(3, 4), (4, 5)]) [](#__codelineno-2-27) def test_answer(x, y): [](#__codelineno-2-28) assert inc(x) == y, "These tests should pass." [](#__codelineno-2-29)[](#__codelineno-2-30)@app.cell [](#__codelineno-2-31)def imports(): [](#__codelineno-2-32) import pytest [](#__codelineno-2-33) return pytest` prints `[](#__codelineno-3-1)============================= test session starts ============================== [](#__codelineno-3-2)platform linux -- Python 3.12.9, pytest-8.3.5, pluggy-1.5.0 [](#__codelineno-3-3)rootdir: /notebooks [](#__codelineno-3-4)configfile: pyproject.toml [](#__codelineno-3-5)collected 4 items [](#__codelineno-3-6)[](#__codelineno-3-7)test_notebook.py::test_fails FAILED [ 25%] [](#__codelineno-3-8)test_notebook.py::test_sanity PASSED [ 50%] [](#__codelineno-3-9)test_notebook.py::MarimoTestBlock_0::test_parameterized[3-4] PASSED [ 75%] [](#__codelineno-3-10)test_notebook.py::MarimoTestBlock_0::test_parameterized[4-5] PASSED [100%] [](#__codelineno-3-11)[](#__codelineno-3-12)=================================== FAILURES =================================== [](#__codelineno-3-13)__________________________________ test_fails __________________________________ [](#__codelineno-3-14) [](#__codelineno-3-15) # content of test_notebook.py [](#__codelineno-3-16) import marimo [](#__codelineno-3-17) [](#__codelineno-3-18) __generated_with = "0.10.6" [](#__codelineno-3-19) app = marimo.App() [](#__codelineno-3-20) [](#__codelineno-3-21) [](#__codelineno-3-22) @app.cell [](#__codelineno-3-23) def _(): [](#__codelineno-3-24) def inc(x): [](#__codelineno-3-25) return x + 1 [](#__codelineno-3-26) return (inc,) [](#__codelineno-3-27) [](#__codelineno-3-28) [](#__codelineno-3-29) @app.cell [](#__codelineno-3-30) def test_fails(inc): [](#__codelineno-3-31)> assert inc(3) == 5, "This test fails" [](#__codelineno-3-32)E AssertionError: This test fails [](#__codelineno-3-33)E assert 4 == 5 [](#__codelineno-3-34)E + where 4 = (3) [](#__codelineno-3-35)[](#__codelineno-3-36)test_notebook.py:17: AssertionError [](#__codelineno-3-37)=========================== short test summary info ============================ [](#__codelineno-3-38)FAILED test_notebook.py::test_fails - AssertionError: This test fails [](#__codelineno-3-39)========================= 1 failed, 3 passed in 0.82s ==========================` Publish to the web - marimo https://docs.marimo.io/guides/publishing/ Publishing notebooks to the web[¶](#publishing-notebooks-to-the-web "Permanent link") ------------------------------------------------------------------------------------- You can publish marimo notebooks to the web as interactive editable notebooks, readonly web apps, or [static documents](https://docs.marimo.io/guides/exporting/). Thanks to [WebAssembly](https://docs.marimo.io/guides/wasm/), you can even share executable notebooks on GitHub Pages or other static sites without paying for backend infrastrcture. This makes it easy to share your work with colleagues, embed executable notebooks in web documentation or educational websites, and more. This guide provides an overview of the various ways to publish marimo notebooks. | Guide | Description | | --- | --- | | [Embedding](https://docs.marimo.io/guides/publishing/embedding/) | An overview of embedding notebooks in other sites | | [From GitHub](https://docs.marimo.io/guides/publishing/from_github/) | Share links to executable notebooks hosted on GitHub | | [From code snippets](https://docs.marimo.io/guides/publishing/from_code_snippets/) | Convert code snippets in Markdown or HTML to interactive notebooks | | [GitHub Pages](https://docs.marimo.io/guides/publishing/github_pages/) | Publish interactive notebooks on GitHub Pages | | [Cloudflare](https://docs.marimo.io/guides/publishing/cloudflare/) | Publish interactive notebooks on Cloudflare | | [Online playground](https://docs.marimo.io/guides/publishing/playground/) | Share links to notebooks using our online playground | | [Community Cloud](https://docs.marimo.io/guides/publishing/community_cloud/) | Save notebooks to our free Community Cloud | | [Self-host WebAssembly notebooks](https://docs.marimo.io/guides/publishing/self_host_wasm/) | Self-hosting interactive WebAssembly (HTML export) notebooks | | [View notebooks on GitHub](https://docs.marimo.io/guides/publishing/view_outputs_on_github/) | Viewing notebook outputs on GitHub | | [Deploy on a backend](https://docs.marimo.io/guides/publishing/deploy/) | Deploying notebooks on backends | | [With MkDocs](https://docs.marimo.io/guides/publishing/mkdocs/) | Publish reactive websites with MkDocs from markdown | | [With Quarto](https://docs.marimo.io/guides/publishing/quarto/) | Publish reactive websites with Quarto from markdown | Embedding - marimo https://docs.marimo.io/guides/publishing/embedding/ There are various ways to embed marimo notebooks in other web pages, such as web documentation, educational platforms, or static sites in general. Here are two ways: * Host on [GitHub Pages](https://docs.marimo.io/guides/publishing/github_pages/) or [self-host WASM HTML](https://docs.marimo.io/guides/publishing/self_host_wasm/), and `` Showing editor controls To show editor controls (such as panels icons and the run button), use the query parameter `show-chrome=true` ### Embedding an existing notebook[¶](#embedding-an-existing-notebook "Permanent link") To embed existing marimo notebooks into a webpage, first, [obtain a URL to your notebook](#creating-and-sharing-playground-notebooks), then put it in an iframe. `[](#__codelineno-3-1)` ### Embedding an existing notebook in read-only mode[¶](#embedding-an-existing-notebook-in-read-only-mode "Permanent link") You can optionally render embedded notebooks in read-only mode by appending `&mode=read` to your URL. `[](#__codelineno-4-1)` ### Embedding from code[¶](#embedding-from-code "Permanent link") You can also embed marimo notebook from its string representation (i.e., the notebook file's code), using the `code` query parameter: `[](#__codelineno-5-1)https://marimo.app?embed=true&show-chrome=false&code=` where `` is the notebook code URI encoded. For example, in JavaScript: `[](#__codelineno-6-1)encodeURIComponent(notebookCode)` #### Using lz compression for large notebooks[¶](#using-lz-compression-for-large-notebooks "Permanent link") When using the `code` query parameter, your notebooks must be no greater than `14 KB`. For large notebooks, `marimo.app` supports lz-compressed notebook code with a URL hash. For example, in JavaScript, you can use the [`lz-string`](https://www.npmjs.com/package/lz-string) package: `` [](#__codelineno-7-1)import { compressToEncodedURIComponent } from "lz-string"; [](#__codelineno-7-2)[](#__codelineno-7-3)const url = `https://marimo.app/#code/${compressToEncodedURIComponent(code)}` `` #### MDX[¶](#mdx "Permanent link") For example, if you are using MDX, you can use the following snippet: ``[](#__codelineno-8-1)const MdxNotebook = (props: { code: string }) => { [](#__codelineno-8-2) return ( [](#__codelineno-8-3)