TIL: Jupytext + MyST for MD ↔︎ Jupyter Notebooks

My workflow for IPYNB development with LLMs using Markdown
TIL
jupyter
markdown
Author

Dan O’Leary

Published

November 2, 2025

The Problem

I leverage LLMs like Claude and Cursor when generating lecture materials. They accelerate my work and help improve the end product with better sample code, etc. But all of those tools still struggle when creating / editing Jupyter notebooks, which is my medium of choice for this work. They can do it, but it is often a slow, cumbersome, more error-prone process. They sometimes resort to writing Python scripts to generate JSON that the ipynb format is based on. It is rapidly improving but not ideal.

But they do know markdown really well, so I went looking for a md to ipynb converter, and found… Jupytext!

I’m now able to easily round-trip markdown to ipynb and back, iteratively collaborating with my LLM of choice (#claude-boi).

What is JupyText?

Jupytext is a sort of swiss army knife for Jupyter notebooks. It converts them to and from plain text formats like Markdown, Python scripts, and R Markdown. It’s bidirectional and structure-preserving - unlike nbconvert (which is a one-way trip), or Pandoc (which can convert notebooks but doesn’t support the MyST format or paired file syncing). Think of it as filling the gap between notebook-first tools (Jupyter) and text-first tools (Quarto, RMarkdown, your text editor).

Installation

For use as a CLI-only tool, uvx is all you need. No install required. Just prefix any of the Jupytext commands that follow with uvx and you are good to go. For example, uvx jupytext --to ipynb lecture.md.

My Round-Trip Workflow

You can start at any point in this process.

  1. Generate lecture.md
  2. Convert: jupytext --to ipynb lecture.md
  3. Edit/execute in Jupyter
  4. Convert back: jupytext lecture.ipynb --to md:myst
  5. Send updated .md to AI for refinements
  6. Repeat

With Section 6 set up, step 4 is handled automatically.

The Format

It’s markdown with some bells and whistles.

Frontmatter (MyST format)

Include frontmatter in your md file to specify how Jupytext should handle conversion. Note that we’re using the myst format…

---
jupytext:
  text_representation:
    extension: .md
    format_name: myst
---

Why MyST1? It’s markdown-native and explicitly distinguishes executable code cells ({code-cell}) from markdown code blocks, preventing AI confusion. You can include other information here, like which IPYNB kernel to use, but I find the four lines above are sufficient.

Code Cells

Use fenced code blocks with the {code-cell} directive:

```{code-cell} ipython3
import pandas as pd
import numpy as np

df = pd.DataFrame({'x': [1, 2, 3]})
```

Force Cell Boundaries

Use +++ on its own line to force cell boundaries. I prefer to do this before and after all headers to put them in their own cell. This is because JupyterLab’s heading collapse does not hide body text in the same cell as the heading, just cells below it.

My AI Prompt Template

When asking an AI to generate jupytext-ready content, I’ll include something like this to specify Jupytext related details.

Format requirements:

- Use MyST-flavored jupytext with this frontmatter:
  ---
  jupytext:
    text_representation:
      extension: .md
      format_name: myst
  ---

Differentiate code that should be executable (have a run button) from those that
are only documentation (python formatted code blocks in markdown cells) by adding
`{code-cell} ipython3` after the opening triple-backticks on any code block.

Also, please use +++ BEFORE and AFTER ALL headers to put each in a separate cell

Other formatting preferences (no bold in lists, blank lines before code blocks, etc.) are handled by a Claude Code rules file that auto-loads when editing markdown.

This gives me clean, immediately-convertible markdown.

Converting with Jupytext

Manual conversion is as easy as…

# Markdown to notebook (--update preserves existing cell outputs)
jupytext --to ipynb --update lecture.md

# Notebook back to markdown (for AI refinement)
jupytext --to md:myst lecture.ipynb

Syncing MD and IPYNB

If you want to get fancy, do the following…

# Sync pair (auto-update both) - advanced
jupytext --set-formats md:myst,ipynb lecture.ipynb

After that every time you save either file in Jupyter, both are generated. Perfect for the round-trip workflow.

For sync-on-save to work, Jupytext must be installed as a Jupyter server extension - not just as a CLI tool. Install it in the same environment as Jupyter itself. Pick your poison, pip, conda, or uv to update your venv accordingly.

In practice, I set up syncing on every lecture project now and rarely run manual conversions.

Resources

Footnotes

  1. MyST Markdown is a powerful publishing system. Similar to Quarto / RMarkdown, it supports cross-references, figure numbering, citations, admonitions (aka callouts), etc. Jupytext provides tools to connect those ecosystems. For example, you can edit Quarto (.qmd) documents as notebooks in Jupyter, and pair .ipynb notebooks with .qmd notebooks. For lecture notebooks, you only need the basics shown here. But if you want more, MyST scales up.↩︎