a post with marimo notebooks
marimo is a reactive Python notebook that runs in the browser via WebAssembly. You can embed interactive marimo notebooks directly in your blog posts — readers can run and edit Python code without installing anything.
al-folio supports two ways to embed marimo notebooks:
- Inline snippets — write Python code directly in your post that becomes interactive
- Iframe embeds — embed hosted marimo notebooks from marimo.app
Inline marimo snippets
The simplest approach is to write Python code blocks directly in your post. When marimo: true is set in the frontmatter, the marimo-snippets library is loaded automatically and converts your code blocks into interactive notebooks running via WebAssembly.
Wrap your code in a <div> with class marimo-notebook-inline and markdown="1". Each fenced code block becomes a separate cell in the notebook:
<div class="marimo-notebook-inline" markdown="1">
```python
import marimo as mo
```
```python
name = mo.ui.text(placeholder="your name", label="Enter your name:")
name
```
```python
mo.md(f"Hello, **{name.value or 'stranger'}**! 👋") if name.value else mo.md("Type your name above!")
```
</div>
This produces the following interactive notebook — type your name and see the greeting update in real time! Powered entirely by WebAssembly in your browser, no server needed:
import marimo as mo
name = mo.ui.text(placeholder="your name", label="Enter your name:")
name
mo.md(f"Hello, **{name.value or 'stranger'}**! 👋") if name.value else mo.md("Type your name above!")
Merging code blocks with markdown cells
You can also include markdown cells alongside Python cells:
<div class="marimo-notebook-inline" data-height="600px" markdown="1">
```md
# Introduction
You can also add markdown cells to your notebooks.
```
```python
import marimo as mo
```
```python
slider = mo.ui.slider(1, 10)
slider
```
```python
slider.value * "🍃"
```
</div>
Result:
# Introduction
You can also add markdown cells to your notebooks.
import marimo as mo
slider = mo.ui.slider(1, 10)
slider
slider.value * "🍃"
A convex optimization demo
Here’s a more advanced example using cvxpy to solve a convex optimization problem with a live visualization. The Chebyshev center of a polygon is the center of the largest circle that fits entirely inside it — a classic problem in computational geometry. Move the slider to shift the right wall and watch the optimal inscribed circle adapt:
import marimo as mo
mo.md(
r"""
**Chebyshev Center** — Given a polygon defined by half-planes $a_i^\top x \leq b_i$, find the largest inscribed circle:
$$\text{maximize} \quad r \qquad \text{s.t.} \quad a_i^\top x_c + \lVert a_i \rVert_2 \, r \leq b_i \;,\; i = 1, \dots, m$$
"""
)
wall = mo.ui.slider(start=1.5, stop=5.0, step=0.1, value=3.5, label="Right wall position:")
wall
import cvxpy as cp
import numpy as np
import matplotlib
matplotlib.use("agg")
import matplotlib.pyplot as plt
w = wall.value
# Polygon as half-planes: Ax <= b
# x >= 0, y >= 0, y <= 3, x + y <= 5, x <= w
A = np.array([[-1, 0], [0, -1], [0, 1], [1, 1], [1, 0]])
b = np.array([0.0, 0.0, 3.0, 5.0, w])
# Chebyshev center: maximize radius of inscribed circle
r = cp.Variable(nonneg=True)
xc = cp.Variable(2)
prob = cp.Problem(
cp.Maximize(r),
[A[i] @ xc + np.linalg.norm(A[i]) * r <= b[i] for i in range(len(b))],
)
prob.solve()
# --- Visualization ---
fig, ax = plt.subplots(figsize=(7, 5))
# Shade the feasible polygon
gx = np.linspace(-0.5, 6, 250)
gy = np.linspace(-0.5, 4, 250)
xx, yy = np.meshgrid(gx, gy)
pts = np.column_stack([xx.ravel(), yy.ravel()])
feasible = np.all(pts @ A.T <= b + 1e-9, axis=1).reshape(xx.shape)
ax.contourf(xx, yy, feasible.astype(float), levels=[0.5, 1.5], colors=["#3b82f6"], alpha=0.15)
ax.contour(xx, yy, feasible.astype(float), levels=[0.5], colors=["#3b82f6"], linewidths=2)
# Draw the largest inscribed circle
if prob.status in ("optimal", "optimal_inaccurate"):
circle = plt.Circle(xc.value, r.value, fill=False, color="#ef4444", lw=2.5, ls="--")
ax.add_patch(circle)
ax.plot(*xc.value, "o", color="#ef4444", ms=9, zorder=5)
ax.annotate(
f"r* = {r.value:.2f}",
xy=xc.value,
xytext=(15, 15),
textcoords="offset points",
fontsize=12,
color="#ef4444",
fontweight="bold",
arrowprops=dict(arrowstyle="->", color="#ef4444"),
)
ax.set_xlabel("$x_1$", fontsize=13)
ax.set_ylabel("$x_2$", fontsize=13)
ax.set_title("Chebyshev Center — Largest Inscribed Circle", fontsize=14, fontweight="bold")
ax.set_aspect("equal")
ax.grid(True, alpha=0.3)
ax.set_xlim(-0.5, 6)
ax.set_ylim(-0.5, 4)
plt.tight_layout()
fig
note: The code can be shown by going to the three dots menu on the top right of the notebook and selecting “Show code”. You can also set data-show-code="true" on the container div to show code by default.
Iframe embeds
For more complex notebooks, you can host them on marimo.app and embed them via iframe using the marimo.liquid include:
{% include marimo.liquid src="https://marimo.app/l/YOUR_NOTEBOOK_ID" %}
Here is a hosted notebook from the marimo team:
You can also customize the embed:
{% include marimo.liquid src="https://marimo.app/l/YOUR_NOTEBOOK_ID" height="800px" mode="edit" caption="My interactive notebook" %}
Include parameters
| Parameter | Default | Description |
|---|---|---|
src | — | URL of the hosted marimo notebook (required) |
height | "600px" | Height of the iframe |
width | "100%" | Width of the iframe |
mode | "read" | "read" (read-only) or "edit" (editable) |
embed | true | Append embed=true query parameter |
caption | — | Caption text displayed below the notebook |
title | — | Accessible title for the iframe |
class | — | Additional CSS classes |
Setting up your own post
To add marimo notebooks to your blog post:
- Add
marimo: trueto your post’s frontmatter — this loads themarimo-snippetslibrary - For inline snippets, wrap Python code blocks in
<div class="marimo-notebook-inline" markdown="1"> - For iframe embeds, use
{% include marimo.liquid src="..." %}
That’s it! Your readers get a fully interactive Python environment right in their browser.
Enjoy Reading This Article?
Here are some more articles you might like to read next: