Exporting to HTML and other formats¶
Export marimo notebooks to other file formats at the command line using
Export to static HTML¶
Export from a running notebook¶
Export the current view your notebook to static HTML via the notebook menu:
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 to HTML at the command line:
or watch the notebook for changes and automatically export to HTML:
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
.
Export to a Python script¶
Export to a flat Python script in topological order, so the cells adhere to their dependency graph.
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 notebook in top to bottom order, so the cells are in the order as they appear in the notebook.
This can be useful to plug into other tools that read markdown, such as Quarto or MyST.
You can also convert the markdown back to a marimo notebook:
Export to Jupyter notebook¶
Export to Jupyter notebook in topological order, so the cells adhere to their dependency graph.
Exporting to PDF, slides, or rst¶
If you export to a Jupyter notebook, you can leverage various Jupyter ecosystem tools. For PDFs, you will
need to have Pandoc and Tex installed. The examples below use uvx
, which you can obtain by installing uv
.
NOTEBOOK=notebook.ipynb
# Convert to PDF using nbconvert
uvx --with nbconvert --from jupyter-core jupyter nbconvert --to pdf $NOTEBOOK
# Convert to web PDF
uvx --with "nbconvert[webpdf]" --from jupyter-core jupyter nbconvert --to webpdf $NOTEBOOK --allow-chromium-download
# Convert to slides
uvx --with nbconvert --from jupyter-core jupyter nbconvert --to slides $NOTEBOOK
# Convert to rst with nbconvert
uvx --with nbconvert --from jupyter-core jupyter nbconvert --to rst $NOTEBOOK
# Generate PNG/PDF of specific cells using nbconvert
uvx --with nbconvert --with jupyter --from jupyter-core jupyter nbconvert --to pdf --execute --stdout $NOTEBOOK \
--TemplateExporter.exclude_input=True
# Use nbconvert programmatically for more control
uv run --with nbconvert python -c "
from nbconvert import PDFExporter
import nbformat
nb = nbformat.read('$NOTEBOOK', as_version=4)
pdf_exporter = PDFExporter()
pdf_data, resources = pdf_exporter.from_notebook_node(nb)
with open('notebook.pdf', 'wb') as f:
f.write(pdf_data)
"
You can also use other tools that work with Jupyter notebooks:
Export to WASM-powered HTML¶
Export your notebook to a self-contained HTML file that runs using WebAssembly:
# export as readonly, with code locked
marimo export html-wasm notebook.py -o output_dir --mode run
# export as an editable notebook
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 betweenrun
(read-only) oredit
(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
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 this reason, we recommend using the online playground if possible: https://marimo.app.
Testing the export¶
You can test the export by running the following command in the directory containing your notebook:
Deploying to GitHub Pages¶
Template repository
You can fork our template repository for deploying multiple notebooks to GitHub Pages. Once you have forked the repository, add your notebooks to the notebooks
/apps
directory.
You can deploy your WebAssembly marimo notebook to GitHub Pages using the following GitHub Actions workflow:
jobs:
build:
runs-on: ubuntu-latest
steps:
# ... checkout and install dependencies
- name: 📄 Export notebook
run: |
marimo export html-wasm notebook.py -o path/to/output --mode run
- name: 📦 Upload Pages Artifact
uses: actions/upload-pages-artifact@v3
with:
path: path/to/output
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
permissions:
pages: write
id-token: write
steps:
- name: 🌐 Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
with:
artifact_name: github-pages
Exporting multiple notebooks¶
In order to export multiple notebooks under the same folder, you can use the following snippet:
files=("batch_and_form.py" "data_explorer.py")
for file in "${files[@]}"; do
without_extension="${file%.*}"
marimo export html-wasm "$file" -o public/"$without_extension".html --mode run
done
Optionally, you can create an index.html
file in the public directory:
echo "<html><body><ul>" > public/index.html
for file in "${files[@]}"; do
without_extension="${file%.*}"
echo "<li><a href=\"$without_extension.html\">$without_extension</a></li>" >> public/index.html
done
echo "</ul></body></html>" >> public/index.html
🏝️ Embed marimo outputs in HTML using Islands¶
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 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.
Islands in action¶
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.
<head>
<!-- marimo js/ccs --
<script type="module" src="https://cdn.jsdelivr.net/npm/@marimo-team/islands@<version>/dist/main.js"></script>
<link
href="https://cdn.jsdelivr.net/npm/@marimo-team/islands@<version>/dist/style.css"
rel="stylesheet"
crossorigin="anonymous"
/>
<!-- fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Fira+Mono:wght@400;500;700&family=Lora&family=PT+Sans:wght@400;700&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/katex@0.16.10/dist/katex.min.css"
integrity="sha384-wcIxkf4k558AjM3Yz3BBFQUbk/zgIYC2R0QpeeYb+TwlBVMrlgLqwRjRtGZiK7ww"
crossorigin="anonymous"
/>
</head>
<body>
<marimo-island data-app-id="main" data-cell-id="MJUe" data-reactive="true">
<marimo-cell-output>
<span class="markdown">
<span class="paragraph">Hello, islands!</span>
</span>
</marimo-cell-output>
<marimo-cell-code hidden>mo.md('Hello islands 🏝️!')</marimo-cell-code>
</marimo-island>
</body>
Generating islands¶
While you can generate the HTML code for islands yourself, it it recommend to use our MarimoIslandGenerator
class to generate the HTML code for you.
marimo.MarimoIslandGenerator
¶
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:
- Find all the code snippets and add them to the generator.
- Build the app.
- Replace all code snippets with the rendered HTML.
- Include the header in the tag.
Example¶
from marimo.islands import MarimoIslandGenerator
generator = MarimoIslandGenerator()
block1 = generator.add_code("import marimo as mo")
block2 = generator.add_code("mo.md('Hello, islands!')")
# Build the app
app = await generator.build()
# Render the app
output = f"""
<html>
<head>
{generator.render_head()}
</head>
<body>
{block1.render(display_output=False)}
{block2.render()}
</body>
</html>
"""
add_code
¶
add_code(
code: str,
display_code: bool = False,
display_output: bool = True,
is_reactive: bool = True,
is_raw: 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
¶
build() -> App
Build the app. This should be called after adding all the code cells.
Returns:
- App: The built app.
from_file
staticmethod
¶
from_file(
filename: str, display_code: bool = False
) -> 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
¶
render_body(
*,
include_init_island: bool = True,
max_width: Optional[str] = None,
margin: Optional[str] = None,
style: Optional[str] = None
) -> str
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
¶
render_head(
*,
version_override: str = __version__,
_development_url: Union[str, bool] = False
) -> 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
¶
render_html(
*,
version_override: str = __version__,
_development_url: Union[str, bool] = False,
include_init_island: bool = True,
max_width: Optional[str] = None,
margin: Optional[str] = None,
style: Optional[str] = None
) -> str
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
¶
Renders a static html MarimoIsland str which displays a spinning initialization loader while Pyodide loads and disappears once the kernel is ready to use.