Putting Jupyter notebooks under source control with Quarto

Posted on Tue 13 January 2026 in Software

Jupyter notebooks are an excellent way to provide interactivity. Extremely helpful when building tutorials and demos. However, putting Jupyter notebooks under source control is messy: the .ipynb format is a JSON-based format that combines content (code and Markdown text) with state information (outputs, version numbers, etc.), and you need to clean up this information before committing or else Git gets very confused.

Also, the fact that text has to live in separate cells from code tends to break down the flow when reading.

This is where Quarto is very useful. Quarto lets you write code and text together in a single .qmd document, a Markdown file. Code is interpolated using a variant on standard Markdown code blocks. The file therefore consists solely of content, no metadata or execution outputs.

Quarto offers a lot of features for publishing notebooks as a website, as a PDF/ePub book, and other formats, but the most useful is its feature to convert easily between .qmd and .ipynb. Here's a Makefile for doing this:

SHELL := /bin/bash
.ONESHELL:

QMD := $(wildcard *.qmd)
IPYNB := $(QMD:%.qmd=build/%.ipynb)
VENV := .venv

notebooks: $(IPYNB)

build/%.ipynb: %.qmd
	. $(VENV)/bin/activate
	mkdir -p build
	quarto convert $< -o $@

clean:
	rm -rf build

.PHONY: all clean

Note that this is a Makefile so the indents need to be tab characters, not spaces.

To "compile" your .qmd files into Jupyter notebooks, simply run

make notebooks

The build/ directory will now contain the notebooks, which you can distribute. It's a good idea to put build/ under .gitignore so that only your .qmd files go under source control.