如何用 marimo islands 把交互式笔记本内容嵌入静态网页?
【免费下载链接】marimoA reactive notebook for Python — run reproducible experiments, query with SQL, execute as a script, deploy as an app, and version with git. Stored as pure Python. All in a modern, AI-native editor.项目地址: https://gitcode.com/GitHub_Trending/ma/marimo
你已有的静态页面(博客、教程、课程站点)想放进 marimo 笔记本里的单元格输出,并且希望读者打开页面后这些内容可以交互,而不是截图或死板的 HTML。marimo 的 islands 功能就是为此设计的:把 marimo 的输出和/或 Python 代码以自定义 HTML 标签的形式嵌入你的页面,页面加载后由 marimo 的响应式运行时接管,内容变为可交互(发布指南 将其列为自托管场景下“把单个单元格输出直接嵌入 HTML 页面”的选项)。注意文档标注:islands 仍是早期功能,API 预计不会变,但在被认定为稳定前可能还有改进。
islands 与常见的 iframe 嵌入不同:没有顶层 app,运行时拿不到父页面的 HTML,只在你用 marimo 自定义标签标出的“岛”上初始化(见 frontend/islands/development.md)。文档也提示这是一个面向集成的高级构建块,适合接到静态站点生成器或文档工具里。
主路径:从代码块生成 island HTML
docs/guides/exporting/webassembly_html.md 给出的核心做法是用MarimoIslandGenerator逐块添加代码、构建应用、再把渲染出的 HTML 拼进页面。文档示例:
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())上例按文档整理(block2的代码以文档为准应为mo.md('Hello, islands!'),这里保留文档原文结构)。执行时要理解几个关键点:
add_code每个代码块调用一次,返回一个可render()的 stub。build()会在本地 marimo 会话中运行这些代码,拿到真实输出;文档源码明确说明build()只能调用一次(marimo/_islands/_island_generator.py)。render_head()生成放进<head>的 JS/CSS 引用,默认使用当前安装的 marimo 版本(version_override参数可指定其他版本);它包含@marimo-team/islands的main.js和style.css(jsDelivr CDN)、Google Fonts 预连接与 KaTeX 样式,这些都由render_head()自动生成,不需要手写。- 第一块是 import,没有展示价值,所以用
block1.render(display_output=False)只保留代码;第二块正常render(),输出和隐藏代码一起渲染。 add_code还支持display_code(是否在 HTML 中展示代码)、display_output、is_reactive(该块是否通过 pyodide 在浏览器里运行)等参数,默认值见源码 docstring。
生成的output.html就是一份可以直接放进静态站点的完整页面。
替代路径:从已有的 marimo .py 笔记本生成
如果你已经有一个 marimo 笔记本文件(marimo 笔记本本身就是纯 Python 文件),可以用from_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)./<notebook-name>.py是文档中的占位符,替换成你的 marimo 笔记本文件路径即可。这条路径有一个明确的限制(文档原话):不经过build()的渲染是 basic rendering,单元格不会真正执行。也就是说,如果需要真实运行结果,走上一条含await generator.build()的路径;如果只需要静态渲染结构,from_file+render_html更省事。
render_html/render_body还有几个影响产物的开关:
include_init_island(默认True):在内容前加一个显示 “Initializing...” 的加载器,Pyodide 加载完成、内核就绪后消失。include_payload(默认False):在 body 末尾追加一段 JSON payload 脚本标签,供 islands 运行时用来水合(hydrate)页面。max_width/margin/style:控制 body 包裹容器的 CSS,style会覆盖前两者。
生成的 HTML 长什么样
无论哪条路径,产物结构一致:<head>里是 marimo JS/CSS 与字体引用,<body>里是用自定义标签定义的岛屿。文档给出的模板(其中<version>需替换为实际版本,与render_head()自动填充的版本保持一致):
<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><script type="application/vnd.marimo.islands+json">{"schemaVersion":1,"appId":"main","cells":[{"cellId":"cell-1","code":"mo.md('Hello, islands!')","outputHtml":"\u003cspan\u003eHello, islands!\u003c/span\u003e","outputMimetype":"text/markdown","reactive":true,"displayCode":false,"displayOutput":true}]}</script>payload 保存了每个单元格的代码、渲染后的输出 HTML、输出 MIME 类型和显示设置;DOM 提供可见的岛屿槽位,payload 提供运行时代码与元数据。文档特别强调:如果你要后处理 island HTML,必须原样保留这个type="application/vnd.marimo.islands+json"的 script 标签及其内容,否则会破坏运行时水合。
验证与限制
- 成功判据来自文档站自带的 islands 示例页(docs/guides/island_example.md):页面打开时先展示预渲染输出;marimo 运行时初始化完成后,“内容变为可交互”(示例页中一个
mo.ui.slider拖动后下方 Markdown 会跟着变化)。include_init_island=True时还会先看到 “Initializing...” 加载器,内核就绪后消失。可以拿这份示例页对照你生成页面的行为。 - 版本一致性:手动维护 head 模板时,
<version>要与生成端一致;用render_head()输出则版本自动跟随安装的 marimo(version_override可覆盖),文档示例页固定用的是0.5.0。 - 稳定性:文档两处标注 islands 是 early feature、尚未稳定,API 预计不变但仍有改进计划。
from_file路径的渲染不执行单元格,只适合 basic rendering;需要真实输出时用build()路径。- 生成 HTML 后如需本地联调,仓库内 frontend/islands/development.md 描述了
pnpm dev:islands的本地开发/生产生成流程(面向 marimo 前端开发者)。
如果你的目标平台是 Jupyter Book,文档给出了现成的集成方式:jupyter-book-marimo插件会把 MyST 的{marimo}指令渲染为 hydrated marimo islands(见 docs/guides/exporting/jupyter_book.md),不必手写上面这套 HTML 拼装逻辑。
【免费下载链接】marimoA reactive notebook for Python — run reproducible experiments, query with SQL, execute as a script, deploy as an app, and version with git. Stored as pure Python. All in a modern, AI-native editor.项目地址: https://gitcode.com/GitHub_Trending/ma/marimo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考