Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/guides/exporting/webassembly_html.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,20 @@ Use `MarimoIslandGenerator` to generate HTML for islands

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.

### Island payloads

`MarimoIslandGenerator.render_html(include_payload=True)` and `render_body(include_payload=True)` include a JSON payload. The payload stores each cell's code, rendered output HTML, output MIME type, and display settings.

The islands runtime uses this payload to hydrate the page. The DOM still provides the visible island slots, and the payload provides the runtime cell code and output metadata.

An emitted payload looks like this. HTML-sensitive characters inside JSON strings are escaped before marimo writes the script tag.

```html
<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>
```

If you post-process island HTML, preserve the script tag with type `application/vnd.marimo.islands+json` and keep its contents unchanged.

### Islands in action

!!! warning "Advanced topic!"
Expand Down
25 changes: 25 additions & 0 deletions frontend/src/core/islands/__tests__/bridge.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
type Base64String = components["schemas"]["Base64String"];
interface TestIslandApp {
id: string;
payloadBacked?: boolean;
cells: { code: string; idx: number; output: string }[];
}
interface TestExportContext {
Expand Down Expand Up @@ -142,6 +143,30 @@ describe("IslandsPyodideBridge", () => {
});
});

it("should ignore trusted export notebook code for a payload-backed app", async () => {
const payloadApp = {
id: "app-1",
payloadBacked: true,
cells: [{ code: "x = 1", idx: 0, output: "<div>1</div>" }],
};
mockParseMarimoIslandApps.mockReturnValue([payloadApp]);
mockGetMarimoExportContext.mockReturnValue({
trusted: true,
notebookCode: "full notebook should be ignored",
});
mockCreateMarimoFile.mockReturnValue("generated payload app");

await (
bridge as unknown as { startSessionsForAllApps(): Promise<void> }
).startSessionsForAllApps();

expect(mockCreateMarimoFile).toHaveBeenCalledWith(payloadApp);
expect(mockStartSessionRequest).toHaveBeenCalledWith({
appId: "app-1",
code: "generated payload app",
});
});

it("should keep synthesized per-app files for multiple reactive apps even when export context exists", async () => {
mockParseMarimoIslandApps.mockReturnValue([
{
Expand Down
Loading
Loading