Embed interactive Python notebooks in static web pages, powered by marimo and WebAssembly.
Bring code snippets to life with marimo snippets: a single-file JavaScript utility that makes it easy to embed interactive Python notebooks in static web pages.
Highlights.
- π markdown-compatible: compatible with
mkdocs
and other markdown preprocessors. - π οΈ extensible: HTML-first design makes it easy to integrate with static site generators like Sphinx
- βοΈ configurable: configure through HTML or JavaScript
- π¦ lightweight: just one small script
Try locally. This repo contains an example (src/index.md
) that you can
try locally; install uv
, then run
uv run --with mkdocs-material mkdocs serve
Use the <marimo-button>
HTML element to
add a button that links to a marimo playground notebook, preloaded with Python
code.
<div>
<marimo-button>
```python
def hello_world():
print("Hello, World!")
```
</marimo-button>
<div>
<script src="https://cdn.jsdelivr.net/npm/@marimo-team/marimo-snippets@1"></script>
This is what it looks like:
<marimo-iframe>
replaces
multiple code snippets with an inline marimo notebook, one cell per code
snippet.
<div>
<marimo-iframe>
```python
import marimo as mo
```
```python
slider = mo.ui.slider(1, 10)
slider
```
```python
slider.value * "π"
```
</marimo-iframe>
</div>
<script src="https://cdn.jsdelivr.net/npm/@marimo-team/marimo-snippets@1"></script>
This will embed an iframe with an interactive slider.
Note
You might wonder why we wrap the <marimo-button>
element with an extra
<div>
. This is related to how different markdown preprocessors might handle
the custom HTML elements. To guarantee that this approach works across most
markdown tools, we need to wrap the custom elements in a block element.
marimo snippets lets you selectively convert code snippets, specifically
<pre>
elements, to marimo notebooks.
These notebooks run via WebAssembly, meaning the browser executes Python code (no backend required). Most packages are supported, including numpy, scipy, scikit-learn, matplotlib, plotly, altair, polars, duckdb, and more. Consult the pyodide documentation for a list of supported packages.
To get started, you'll need to load the marimo snippets script:
<script src="https://cdn.jsdelivr.net/npm/@marimo-team/marimo-snippets@1"></script>
Add an "open-in-marimo" button to pre
elements with <marimo-button>
:
<marimo-button>
<pre>
def hello_world():
print("Hello, World!")
</pre>
</marimo-button>
This renders the <pre>
element but adds a button that opens the code
in the marimo playground.
This is a demo site.
```python
a = 1
b = 2
```
This adds a button that links to a marimo playground notebook preloaded
with Python code.
<div>
<marimo-button>
```python
def hello_world():
print("Hello, World!")
```
</marimo-button>
</div>
If you are using a static site generator like mkdocs, you can users code fences
instead of raw <pre>
elements. You may need to wrap the entire block in a
<div>
, and you may need to add the md_in_html
extension.
Because of its HTML-first design, marimo snippets works out of the box with many static site generators like mkdocs. However, marimo snippets does not work out of the box with all SSGs; for example, it does not work out of the box with Sphinx.
marimo-snippets does not play well with MDX out of the box. To embed marimo notebooks in MDX, see the marimo documentation.
To our community: we'd love your help in developing plugins for other SSGs like Sphinx that target marimo snippets.
- If you develop a plugin, let us know and we'll link to it from our README.
- If you have questions while developing a plugin, reach out on GitHub issues.
marimo snippets is implemented as a wrapper around marimo's online playground. In some cases, when building plugins for SSGs, you may be better served by generating working with playground URLs directly, instead of using marimo-snippets. Consult the marimo docs on embedding playground notebooks to learn more.
marimo snippets can be configured in two ways: globally, or locally for individual snippets.
Default button configuration:
{
elements: ['pre'],
title: 'Open code in an interactive playground',
position: 'absolute',
top: '0.5rem',
right: '0.5rem',
border: 'none',
borderRadius: '4px',
padding: '4px 8px',
margin: '-4px 22px',
cursor: 'pointer',
zIndex: '10',
filter: 'grayscale(100%)',
icon: '<img src="https://cms.marimo.io/icons/favicon.svg" alt="icon" width="20" height="20">',
};
Default iframe configuration:
{
elements: ['pre'],
height: '400px',
width: '100%',
border: '1px solid #ddd',
borderRadius: '4px',
margin: '1rem 0',
showCode: 'true',
url: 'https://marimo.app',
paramName: 'code'
};
To configure snippets globally, include the following script element before loading the marimo snippets script.
<!-- Optionally configure how buttons and iframes are rendered. -->
<!-- Configuration must come _before_ the main extractor script is loaded. -->
<script type="text/x-marimo-snippets-config">
configureMarimoButtons({title: "Open in a marimo notebook"});
configureMarimoIframes({height: "400px"});
</script>
<script src="https://cdn.jsdelivr.net/npm/@marimo-team/marimo-snippets@1"></script>
To configure an a per-element basis, use data attributes:
<marimo-iframe data-height="600px" data-show-code="false">
...
</marimo-iframe>