Skip to content

WebAssembly HTML

Export your notebook to a self-contained HTML file that runs using WebAssembly.

Easiest way to share interactive notebooks

For the simplest way to share interactive notebooks online, including WebAssembly notebooks, use molab.

# 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 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 that WebAssembly notebooks have limitations; in particular, many but not all packages work.

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, use molab, publish to GitHub Pages, Cloudflare, or self-host.

Deploying to Cloudflare

You can include --include-cloudflare for deploying to Cloudflare. For example:

marimo export html-wasm notebook.py -o my_app/dist --include-cloudflare

To run locally, run:

npx wrangler dev

To deploy to Cloudflare, run:

npx wrangler deploy

Testing the export

You can test the export by running the following command in the directory containing your notebook:

cd path/to/output_dir
python -m http.server

Including data files

See the docs for mo.notebook_location to learn how to include data files in exported WASM HTML notebooks.

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 site/"$without_extension".html --mode run
done

Optionally, you can create an index.html file in the public directory:

echo "<html><body><ul>" > site/index.html
for file in "${files[@]}"; do
  without_extension="${file%.*}"
  echo "<li><a href=\"$without_extension.html\">$without_extension</a></li>" >> site/index.html
done
echo "</ul></body></html>" >> site/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.

Generating islands

Use MarimoIslandGenerator to generate HTML for islands

Example

import asyncio
import sys
from marimo import MarimoIslandGenerator

if sys.platform == 'win32':
    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

async def main():
    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>
    """
    print(output)
    # Save the HTML to a file
    output_file = "output.html"
    with open(output_file, "w", encoding="utf-8") as f:
        f.write(output)

if __name__ == '__main__':
    asyncio.run(main())
from marimo import MarimoIslandGenerator

# Create the generator from file
generator = MarimoIslandGenerator.from_file("./<notebook-name>.py", display_code=False)

# Generate and print the HTML without building
# This will still work for basic rendering, though without running the cells
html = generator.render_html(include_init_island=False)
print(html)
# Save the HTML to a file
output_file = "output.html"
with open(output_file, "w", encoding="utf-8") as f:
    f.write(html)

Any relevant .html that gets generated can be run through the development.md file instructions.

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&amp;family=Lora&amp;family=PT+Sans:wght@400;700&amp;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>

marimo.MarimoIslandGenerator

MarimoIslandGenerator(app_id: 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:

import asyncio
import sys
from marimo import MarimoIslandGenerator

async def main():
    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>
    """
    print(output)
    # Save the HTML to a file
    output_file = "output.html"
    with open(output_file, "w", encoding="utf-8") as f:
        f.write(output)

if __name__ == '__main__':
    asyncio.run(main())

You can also create the generator from a file:

from marimo import MarimoIslandGenerator

# Create the generator from file
generator = MarimoIslandGenerator.from_file(
    "./<notebook-name>.py", display_code=False
)

# Generate and print the HTML without building
# This will still work for basic rendering, though without running the cells
html = generator.render_html(include_init_island=False)
print(html)
# Save the HTML to a file
output_file = "output.html"
with open(output_file, "w", encoding="utf-8") as f:
    f.write(html)
PARAMETER DESCRIPTION
app_id

The optional identifier of the app, defaults to main.

TYPE: str DEFAULT: 'main'

has_run instance-attribute

has_run = False

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: str | None = None,
    margin: str | None = None,
    style: str | None = 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: 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: str | bool = False,
    include_init_island: bool = True,
    max_width: str | None = None,
    margin: str | None = None,
    style: str | None = 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

render_init_island() -> 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.