marimo(マリモ)は、セルの実行順序に結果が左右されないPython向けノートブックです。ファイルはJSONではなく普通の.pyとして保存され、あるセルの変数を書き換えると、それを使う下流のセルが自動で追従します。コマンド例の検証環境は、2026年10月26日時点の macOS・Python 3.13・marimo 0.25.2 です。製品仕様や公開日は公式資料を参照しています。
まとめ
- marimo のノートブックは
.pyファイルそのものです。python nb.pyでスクリプトとして実行でき、実行結果の更新によるGit差分は生じません。 - 0.25.2 の
marimo convertは、ipynb で使い回していたdfをdf_1や_dfに自動で改名し、!pip installをPEP 723の依存へ移します。変換直後のmarimo checkは終了コード0でした。 - SQLセルの依存は標準インストールに入っていません。
pip install 'marimo[sql]'が必要で、mo.sqlの戻り値は既定でPolarsのDataFrame(Polars未導入ならpandas)です。 - Jupyterをやめなくても使えます。
marimo-jupyter-extensionを入れると JupyterLab のランチャーから marimo を開け、JupyterHub でも動きます。 marimo runは既定で認証なし(実測でHTTP 200)、marimo editは既定でトークン認証(ログイン画面へ303)です。外部公開時は必ずトークンを指定します。
marimoとJupyterの違い:保存形式と実行モデル
marimo は marimo-team が Apache-2.0 で公開しているオープンソースのノートブックで、PyPI の最新版は 0.25.2(2026-10-19公開)、対応Pythonは 3.10 以上、GitHub APIで見たスター数は 23,322(2026-10-26時点)です。2025年10月30日にCoreWeaveが開発元のMarimo Inc.の買収で最終合意し、ノートブックは今後も無料・寛容なライセンスで提供すると発表しています。Jupyter との違いは見た目ではなく、ファイル形式と実行モデルの2点です。
保存形式は普通のPythonスクリプト
marimo が書き出すファイルは、セルが関数として並んだだけのPythonコードです。次はスライダーを1つ置いて合計を計算するノートブックの中身そのものです。
import marimo
__generated_with = "0.25.2"
app = marimo.App(width="medium")
@app.cell
def _():
import marimo as mo
return (mo,)
@app.cell
def _(mo):
n = mo.ui.slider(1, 10, value=3, label="件数")
n
return (n,)
@app.cell
def _(n):
total = sum(range(n.value + 1))
print("total =", total)
return (total,)
if __name__ == "__main__":
app.run()
関数の引数がそのセルの入力、return がそのセルの出力です。python nb.py と打つと、スライダーの既定値 3 が使われて total = 6 と出力されました(終了コード0)。UI部品を含むノートブックが、そのままバッチ処理として使えます。実行回数や出力結果はファイルに残りません。
Git差分に出る行数の比較
ipynb は実行結果と実行時刻をファイルに書き戻します。2セルのノートブックを nbclient 0.11.0 で実行してコミットし、threshold = 10 を 12 に変えて再実行すると、git diff --numstat は ipynb の変更を「10行追加・10行削除」と報告しました。意味のある変更はソースの該当行と出力値([12, 15] → [15])の2行だけで、残る8行は iopub.execute_input や shell.execute_reply といった実行時刻のメタデータです。同じ変更を marimo の .py に入れると、差分は該当の1行だけでした。
Jupyterとの比較表
| 項目 | Jupyter | marimo |
|---|---|---|
| 保存形式 | .ipynb(JSON・出力込み) | .py(コードのみ) |
| 実行順 | 実行した順 | 変数の依存関係の順 |
| 同じ変数の再定義 | どのセルでも可 | セルをまたいで不可 |
| UI部品 | ipywidgets+コールバック | mo.ui(コールバック不要) |
| スクリプト実行 | nbconvert等で変換 | python nb.py |
| アプリ化 | 別ツールが必要 | marimo run |
| マジック・!コマンド | 使える | 非対応(代替あり) |
再定義の禁止とマジック非対応が、移行で実際に手が止まる2点です。どちらも後述の移行章で対処法を示します。
インストールとCLIの全体像
導入は pip か uv で1行です。Python 3.10 以上が必要です(Pythonの切り替えはpyenvでPythonのバージョンを切り替える手順|shimの仕組みとビルド失敗の対処、uv自体はuvとは?Pythonの環境構築・パッケージ管理・バージョン管理の使い方を徹底解説を参照)。
# pip の場合(記事の検証版を導入)
pip install 'marimo==0.25.2'
# uv の場合(作成・有効化済みの仮想環境で実行)
uv pip install 'marimo==0.25.2'
marimo --version # => 0.25.2
marimo edit notebook.py # 新規作成してブラウザエディタを開く
marimo tutorial intro # チュートリアル
用途別に分かれた6つのextras
extraは lsp・mcp・otel・recommended・sandbox・sql の6つで、素の pip install marimo では SQLセルも ipynb書き出しも動きません。
| やりたいこと | 追加で入れるもの | 中身(0.25.2) |
|---|---|---|
| SQLセル | marimo[sql] |
duckdb・polars[pyarrow]・sqlglot[c] |
| ipynbへ書き出し | nbformat |
本体に同梱されていない |
| 分離環境で実行 | marimo[sandbox] |
uv 0.9.21以上 |
| 補完・ruff連携 | marimo[lsp] |
python-lsp-server・python-lsp-ruff |
| まとめて入れる | marimo[recommended] |
sql・sandbox・altair・ruff・nbformat等 |
nbformat なしで marimo export ipynb を打つと「nbformat is required to convert marimo notebooks to ipynb.」で止まります。迷ったら recommended で一括導入します。
13個のCLIコマンドと使い分け
0.25.2 の marimo --help が出すコマンドは13個です。
| コマンド | 用途 |
|---|---|
marimo edit |
ブラウザエディタで作成・編集 |
marimo run |
読み取り専用のアプリとして起動 |
marimo convert |
ipynb・Markdown・py:percentから変換 |
marimo export |
html・html-wasm・ipynb・md・pdf・script等へ |
marimo check |
静的チェックと整形 |
marimo cache |
ディスクキャッシュの場所と容量を表示 |
marimo pair |
起動中のノートブックをAIエージェントから操作 |
marimo new / config / env |
新規作成/設定表示/環境情報 |
marimo recover / tutorial / shell-completion |
JSONから復旧/チュートリアル/補完 |
既定ポート2718と使用中のときの動き
--port を省略すると、marimo は 2718 番で起動します。2つ目を同時に起動すると 2719 番にずれて立ち上がりました。一方、--port 2718 を明示して使用中だった場合はずらさず、「address already in use」を出して終了コード3で終わります。プロキシで番号を決め打ちする運用では --port を明示し、起動失敗を監視で拾ってください。
UI要素と依存セルの自動再計算
mo.ui には37個の要素があります(0.25.2 で dir(mo.ui) の公開名を数えた値)。スライダーやドロップダウンを置くと、値が変わった瞬間にその値を参照しているセルだけが再計算されます。コールバックは書きません。
| 分類 | 主な要素 |
|---|---|
| 数値・範囲 | slider・range_slider・number |
| 選択 | dropdown・multiselect・radio・checkbox・switch |
| 文字・コード | text・text_area・code_editor |
| データ | table・dataframe・data_editor・file・file_browser |
| グラフ | altair_chart・plotly・matplotlib・anywidget |
| 操作 | button・run_button・form・refresh・batch |
Plotly の色指定はPlotlyの色(color)指定と使い方|パレット一覧・v7の色書式・React・画像出力にまとめています。
再計算のタイミング制御(formとmo.stop)
mo.ui.text は debounce の既定値が True で、Enterキーかフォーカスが外れた時点で値が確定します。複数の入力をまとめて1回で確定させたいときは .form() を使い、送信ボタンを押すまで値を確定させません。
import marimo as mo
# 送信ボタンを押したときだけ値が確定する
query = mo.ui.text(label="検索語").form()
query
# 値が未入力のあいだは下流のセルを止める
mo.stop(not query.value or not query.value.strip(), mo.md("検索語を入力してください"))
hits = [n for n in ("東京", "大阪", "福岡") if query.value in n]
hits
mo.stop(predicate, output) は第1引数が真のときにセルの実行を打ち切り、第2引数を表示します。学習のように1回が重い処理は mo.ui.run_button() と組み合わせ、ボタンを押したときだけ実行させます。
DuckDBを使うSQLセルの書き方
mo.sql() でSQLを書けますが、素の pip install marimo の環境では ManyModulesNotFoundError: The following packages are required to execute sql: duckdb, sqlglot で停止しました。pip install 'marimo[sql]' を入れると、duckdb 2.0.0・polars 2.1.0・sqlglot 30.24.0 が入って動きます。
import marimo as mo
import polars as pl
sales = pl.DataFrame({
"店舗": ["東京", "大阪", "東京", "福岡"],
"金額": [120, 340, 80, 560],
})
top = mo.sql(
f"""
SELECT 店舗, SUM(金額) AS 合計
FROM sales
GROUP BY 店舗
ORDER BY 合計 DESC
"""
)
Pythonの変数名がそのままテーブル名になり、FROM sales で DataFrame が引かれて福岡560・大阪340・東京200が返りました。戻り値は polars.dataframe.frame.DataFrame でした。出力形式の既定は auto(Polars、無ければpandas)で、公式のSQLガイドでは大きなデータには native(DuckDBのまま返す)を勧めています。もう1点、整数列の SUM は decimal[38,0] 型で返りました。後続で整数として扱うなら CAST(SUM(金額) AS BIGINT) と書いておくと型の食い違いを防げます。
裏はDuckDBなので、CSVやParquetを直接クエリする書き方も使えます。詳しくはDuckDBの使い方:インストールからCLI・Python・ファイル読み込みまでの手順【1.5.6対応】、戻り値の扱いはPolarsとは?pandasとの違い・高速化の仕組みと実務での採用判断【実装目線】を参照してください。
重い処理を抑える2種類のキャッシュ
上流を触るたびに重い処理が走るのを抑える仕組みが2種類あり、寿命が違います。どちらもセルの中で使う前提です。共通セルで import marimo as mo と import time を実行してから、以下の例を別セルに書きます(同じ import を別セルで繰り返すと再定義エラーになります)。素のPythonスクリプトから呼ぶと ContextNotInitializedError などで失敗しました。
mo.cache=プロセス内だけのメモリ保存
@mo.cache は、関数の引数に加えて参照している変数とノートブックのコードをキーにメモリへ保存します。0.5秒スリープする関数を2回呼ぶと、1回目 0.503秒、2回目 0.000476秒でした。ノートブックを別プロセスで起動し直すと1回目は再び 0.578秒かかり、キャッシュは残りません。
@mo.cache
def slow(n):
time.sleep(0.5)
return n * n
mo.persistent_cache=ディスクに残る保存
プロセスをまたいで残すなら mo.persistent_cache です。with ブロック内で作った変数をまとめて保存します。1秒スリープを含むブロックは、1回目 1.056秒、プロセスを終了して起動し直した2回目が 0.007秒でした。
with mo.persistent_cache(name="demo"):
time.sleep(1.0)
heavy = sum(range(10**6))
保存先はノートブックと同じ階層の __marimo__/cache/demo/ で、pickle として書き出されます。0.25系で増えた marimo cache size . を打つと「228 B 1 entry」のように容量と件数が出ます。生成物なので .gitignore に __marimo__/ を足しておきます。
Jupyterノートブックからの移行手順
既存の .ipynb は marimo convert で変換します。入力は ipynb のほか、{python} フェンスを含むMarkdown、py:percent形式の .py も受け付けます(公式のJupyter利用者向けガイド)。
marimo convert legacy.ipynb -o legacy.py
convertが自動で直すもの:変数名の重複と!pip
0.25.2 で、Jupyterでよくある書き方を詰めたノートを変換しました。df = [1, 2, 3] のあと別セルで df = [x * 10 for x in df] と上書きし、さらに後のセルで使う形です。変換後は2つ目が df_1 に改名され、後続セルの参照も df_1 に書き換わっていました。他のセルから参照されない一時的な df は _df(セル内だけの変数)になります。変換結果の marimo check は終了コード0、実行結果も元と同じ 6 と 60 でした。
Markdownセルは mo.md() を呼ぶセルに変わり、コードを畳む @app.cell(hide_code=True) が付きます。!pip install polars はファイル冒頭の PEP 723 ヘッダ dependencies = ["polars"] へ移され、元の行はコメントになります。ipynb 側の出力は捨てられます。
マジックコマンドの置き換え
marimo は IPython のマジックと ! で始まるシェル実行に対応していません。convert は %matplotlib inline を「自動で対応済み」としてコメント化し、%%time は「not supported」と注記してコメント化しました。動かすには次のように書き換えます。
| Jupyterでの書き方 | marimoでの置き換え |
|---|---|
!pip install |
パッケージ管理サイドバー(PEP 723に記録) |
!ls などのシェル |
subprocess.run |
%cd / %env |
os.chdir() / os.environ |
%matplotlib inline |
不要(自動で表示) |
%time / %%time |
time.perf_counter() / timeit |
%debug |
breakpoint() |
手で書くときの変数名重複とmarimo check
convert が改名してくれるのは変換のときだけです。変換後に marimo 上で同じ名前を別セルに書くと、再定義エラーになります。marimo check にかけると次のように出ます。
critical[multiple-definitions]: Variable 'df' is defined in multiple cells
--> dup.py:7:1
7 | def _():
8 | df = 1
| ^
...
13 | def _():
14 | df = 2
| ^
hint: Variables must be unique across cells. Alternatively, they can be private with an underscore prefix (i.e. `_df`.)
エラーがあると終了コード1、無ければ0なので、そのままCIに入れられます。警告も失敗にしたい場合は --strict、機械処理には --format json("code": "MB002" のような規則コード付き)を使います。セル内だけで使う変数は _df のようにアンダースコアで始めれば重複できます。
ローカルモジュールの検索パスと自動リロード
自作モジュールを import するなら、pyproject.toml にプロジェクト設定を書きます。相対パスは絶対パスへ解決されて読み込まれます。
[tool.marimo.runtime]
pythonpath = ["libs"]
auto_reload = "autorun"
auto_reload の既定は off で、モジュールを編集してもノートブックに反映されません。lazy は変更を検知したセルを再実行待ちにし、autorun は自動で走らせます。モジュールを書きながら確かめる作業では autorun を選びます。
JupyterLab・JupyterHubでのmarimoの導入と設定
全面移行しなくても、既存のJupyter環境に marimo を足せます。公式の marimo-jupyter-extension(0.5.0・PyPIへの登録は2026-09-29 UTC)を入れると、JupyterLab のランチャーに marimo のアイコンが出て、.ipynb の右クリックから marimo 形式へ変換でき、_mo.py で終わるファイルはダブルクリックで marimo で開きます。
uv pip install 'marimo[sandbox]>=0.25.0' marimo-jupyter-extension jupyterlab
jupyter lab
この構成(JupyterLab 4.6.4・jupyter-server-proxy 4.6.0)で起動し、/marimo/ を開くとHTTP 200で marimo のエディタが返りました。ログを見ると、拡張は marimo を空きポートで --base-url /marimo --token --sandbox=uv 付きで起動し、Jupyter側からプロキシしています。marimo とJupyterの間にもランダムなトークンが付くので、Jupyterの認証を外から回り込まれる構成にはなりません。Jupyter本体の導入はJupyter Notebookのインストール手順|7.6系をpip・uv・condaで入れる方法を参照してください。
JupyterHub では、拡張を Jupyter 側の環境に、marimo を利用者の環境に入れます。jupyterhub_config.py で marimo の場所やサンドボックスの方式を指定でき、Pixiを使う場合は marimo 0.25.0 以上と pixi 0.80 以上が必要です。
c.MarimoProxyConfig.marimo_path = "/opt/bin/marimo"
c.MarimoProxyConfig.sandbox = "pixi" # 既定は "uv"。None で無効
c.MarimoProxyConfig.timeout = 120 # 起動待ち(既定60秒、pixiは300秒)
ハブ自体の構築や認証はJupyterHubとは?JupyterLabとの違い・構築手順と6.0の変更点で扱っています。
アプリとして配る:marimo runとWASM書き出し
marimo run notebook.py でサーバが立ち、コードが見えない読み取り専用の画面になります。Streamlit のようにアプリ用のコードを書き直す必要はありません(考え方の違いはStreamlitとは?読み方・デメリットとできないこと、使い方とライセンス)。
公開前に確認すべき4つの既定値
| オプション | marimo run の既定 |
意味 |
|---|---|---|
--host |
127.0.0.1 | 外部から届かせるには明示が必要 |
--token |
no-token | 認証なし(edit は token が既定) |
--include-code |
無効 | ソースはブラウザへ送られない |
--headless |
無効 | サーバ用途では付けてブラウザ起動を抑止 |
注意すべきは認証です。オプションなしで起動して / を取得すると、marimo run はHTTP 200でアプリを返し、marimo edit は303で /auth/login へ転送しました。marimo run app.py --host 0.0.0.0 とだけ打つと、そのポートに届く相手は誰でも操作できます。--token-password-file でトークンを与えるか、リバースプロキシ側で認証をかけてください。プロキシ配下では --base-url と --proxy を使います。
marimo run app.py --host 0.0.0.0 --port 8080 --headless \
--token-password-file /run/secrets/marimo_token
サーバを立てずに配るWASM書き出し
marimo export html-wasm は、ブラウザ内のPythonで動く静的サイトを書き出します。GitHub Pages 用の .nojekyll も入ります(ブラウザで分析を動かす仕組みはDuckDB WASMとは?ブラウザでSQL分析を実行する仕組みと実装・採用判断も参照)。
marimo export html-wasm notebook.py -o site --mode run
python -m http.server --directory site
出力は27MBで、Python実行系のPyodideは cdn.jsdelivr.net から読み込みます。0.25系で増えた --offline を付けると実行系とパッケージも同梱され、47MB(うちPyodide 13MB・パッケージ7MB)になりました。インターネットに出られない社内網へ置く場合はこちらです。ただし --offline はパッケージ解決にPlaywrightとChromiumを使い、未導入だと「Playwright is required」で止まります。
もう1点、--mode run で --show-code を付けない既定でも、index.html には notebookCode という項目でノートブック全文が埋め込まれていました。ブラウザで実行する以上、コードを送らざるを得ないためです。コードを見せたくない用途にWASM書き出しは使えず、ソースを送らない marimo run を選びます。
依存ごと固定する--sandboxの挙動
--sandbox はノートブック冒頭のPEP 723メタデータを読み、隔離環境で実行します。手元に polars 2.1.0 が入った状態で、polars==1.33.1 を指定したノートブックを marimo export html --sandbox にかけると、出力は「polars 1.33.1」でした。0.25.2 では uv のキャッシュ(~/.cache/uv/environments-v2/)にノートブック専用の環境を作り、uv run --active --no-project --python <その環境> で起動します。--sandbox pixi を指定するとPixiで環境を作ります(Pixiの基本はPixiとは?Python・conda対応のパッケージ管理ツールの使い方とuv・condaとの違い)。
# /// script
# requires-python = ">=3.12"
# dependencies = [
# "marimo",
# "polars==1.33.1",
# ]
# ///
AIエージェントとつなぐmarimo pair
marimo pair は、起動中のノートブックにClaude CodeやCodexなどのエージェントがセルを足し、実行するためのコマンドです。公式ガイドでは Claude Code・Codex・OpenCode が連携先に挙がっています。手順は次のとおりです。
marimo edit nb.py --no-token # サーバを起動しブラウザで開く
marimo pair notebook list # 開いているノートとsession_idを確認
marimo pair execute --url http://localhost:2718 --session sess-xxxx --code-file - <<'PY'
import marimo._code_mode as cm
async with cm.get_context() as ctx:
cid = ctx.create_cell("double = total * 2\nprint('double =', double)")
ctx.run_cell(cid)
PY
実測では stdout に「double = 12」と返り、追加したセルが nb.py にも書き込まれました。つまずいた点が2つあります。ブラウザでノートを開くまでセッションが作られず、notebook list は空でした。また --session には一覧の session_id(sess- で始まる値)を渡します。id(s_ で始まる値)を渡すと「No session」で失敗しました。トークン認証のサーバでは --token-file を付けます。
設定ファイルとエディタ連携
設定の優先順位は、ノートブックのPEP 723メタデータ、プロジェクト設定、ユーザ設定の順です。ユーザ設定の既定の保存先は ~/.config/marimo/marimo.toml、プロジェクト設定は pyproject.toml の [tool.marimo.*]、ノート単位の設定はPEP 723ヘッダの [tool.marimo.*] に書きます。marimo config show では「Project overrides」と「User config」に分かれて表示されます。チームで揃えたい設定はリポジトリに入る pyproject.toml 側へ置きます。エディタは公式のVS Code拡張 marimo-team.vscode-marimo があり、リンタ連携は [tool.marimo.language_servers.pylsp] で ruff や mypy を個別に切り替えます。
marimoを選ぶ場面とJupyterに残す場面
marimo に向くのは、ノートブックをGitでレビューする、同じ分析を定期的にスクリプトとして回す、社内向けにUI付きの画面として配る場合です。集計結果を経営層向けのダッシュボードとして常設したい段階なら、ノートブックの外でBIツールに載せる選択肢も比べてください(BIツール導入支援)。
Jupyterに残すべきなのは、次のどれかに当てはまる場合です。
- ipywidgets や独自のJupyter拡張、Rなど他言語カーネルに業務が依存している(marimo はPython専用)
- 出力込みの
.ipynbをそのまま報告書として配っている(marimo の.pyは出力を持たない) - 大きなデータを1セルずつ試行錯誤し、セルを書き換えるたびに下流が走ると困る(自動実行を切る設定はあるが、それならJupyterの方が素直)
両方を使う組織では、前章の拡張でJupyterLabの中に marimo を同居させ、既存の資産はJupyterのまま、新規の分析と配布物から marimo で書く段階的な導入を勧めます。
よくある質問
marimoは無料ですか?商用利用できますか?
無料で、ライセンスは Apache-2.0(0.25.2 のPyPIメタデータ)なので商用利用できます。CoreWeaveによる買収の発表でも、ノートブックは無料・寛容なライセンスのまま提供すると明記されています。クラウドで動かす molab も無料で、既定で4 CPU・32GB RAMが割り当てられ、ノートは公開扱いですが既定では検索・一覧に出ません。
marimoのインストールにPythonのバージョン制約はありますか?
0.25.2 は Python 3.10 以上が必要です。Python 3.9 を指定して uv pip compile で解決させると、エラーは出ずに marimo==0.17.6 が選ばれました。古い版が黙って入るので、導入後に marimo --version で確かめてください。
marimoのノートブックはJupyterに戻せますか?
marimo export ipynb notebook.py -o notebook.ipynb で戻せます。nbformat が別途必要です。UI要素など一部の機能はJupyter上では動きません。
marimo runで公開したアプリからコードは見えますか?
既定では見えません(--include-code が既定で無効)。WASM書き出しの静的サイトだけは、前述のとおり全文が入ります。
marimoのバージョンはどの程度の頻度で上がりますか?
PyPIには393版があり、直近では2026年9月23日に0.25.0、10月1日に0.25.1、10月19日に0.25.2が公開されています。公開間隔は一定ではありません。0.x系なので、チームで使うなら pyproject.toml や PEP 723 ヘッダで版を固定してください。