Open-source project
jupytext/jupytext avatar
jupytext/jupytext

Jupytext: text notebooks and paired .ipynb files for Git-friendly Jupyter work

Jupyter Notebooks as Markdown Documents, Julia, Python or R scripts

7,253 stars427 forksPythonMIT

At a glance

What is it?
Jupytext stores notebooks as plain .py or .md files and can keep a paired .ipynb alongside them. It solves diff and merge pain in version control, but outputs still live in the binary file.
Who is it for?
Adopt Jupytext if your notebooks live in Git and your team wants readable diffs, or if you want to edit notebooks in an IDE and run them in JupyterLab. Do not adopt it if you need outputs versioned in the same file as inputs, or if your workflow depends on notebook-only features that the text formats do not carry.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 9 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem Jupytext solves: .ipynb files are JSON, and JSON diffs are unreadable

A .ipynb file is JSON. It holds cell sources, cell types, execution counts, outputs and metadata in one document. When two people edit the same notebook, the diff shows JSON structure rather than the code that changed, and a merge conflict is a conflict between two JSON documents. Jupytext's answer is to represent the same notebook in a text format that a human and a diff tool can read.

The README describes the pitch directly: notebooks as plain text documents, editable in a favorite IDE, with clear and meaningful diffs under version control. The target reader is anyone who keeps notebooks in Git, or who wants to refactor notebook code in an editor rather than in a browser cell.

It is not a replacement for the notebook format. Jupytext reads and writes .ipynb; it adds other representations of the same content. That distinction matters for deciding whether the tool fits, because it means the .ipynb file does not go away unless you choose to exclude it from version control.

Text notebooks: the py:percent format and the Markdown family

A text notebook is the notebook inputs written as a script or a Markdown document. The README gives the percent format as the example. A Python notebook in py:percent has a .py extension and looks like a normal Python file with comment markers delimiting cells:

python
# %% [markdown]
# This is a markdown cell

# %%
def f(x):
  return 3*x+1

The README states that only the notebook inputs, and optionally the metadata, are included. That is the trade-off in one sentence: a text notebook is clean in Git because outputs are not there, and it loses those outputs when the notebook is closed. The percent format is recommended for notebooks that mostly contain code and is available for Julia, Python, R and other languages.

For documentation-oriented notebooks, the README points to Markdown-based formats with a .md extension, and names Myst Markdown, Quarto Markdown and Pandoc Markdown as options depending on what you plan to do with the notebook. The demo directory in the repository carries the same notebook in several of these shapes, including World population.ipynb, World population.pct.py, World population.md, World population.myst.md and World population.pandoc.md. That layout is the clearest evidence of how the project thinks about formats: one notebook, many encodings.

Paired notebooks: keeping .ipynb outputs while versioning the .py

A paired notebook is two files that contain the same notebook in different formats, for example .ipynb and .py. The README describes the round trip. You edit the .py version, select reload notebook from disk in Jupyter, and the inputs come back. Outputs are reloaded from the .ipynb file if it exists. The .ipynb version is updated or recreated the next time you save the notebook in Jupyter.

The README also notes that reloading can be automated by installing the Jupyter Collaboration extension. That is a pointer to another project, not a feature inside Jupytext, so treat it as a separate dependency decision.

This pairing is what makes the version-control story workable. You commit the .py file and get ordinary script diffs. The .ipynb file can be excluded from version control if you do not want outputs versioned; the README says Jupytext will recreate those .ipynb files locally when users open and save the .py notebooks. The cost is two files per notebook on disk and a rule your team has to follow about which one is authoritative.

Installing Jupytext and pairing a first notebook

Install Jupytext in the Python environment you use for Jupyter. The README gives two options:

bash
pip install jupytext

or, with conda:

bash
conda install jupytext -c conda-forge

Then restart your JupyterLab server. According to the README, Jupytext is activated when .py and .md files show a Notebook icon and can be opened as notebooks with a right click in JupyterLab. If the icon is missing, the extension is not active and the pairing commands will not appear.

The in-editor path is the command palette. The README names the command Pair Notebook with percent Script. For a whole directory, the README gives a configuration file at the root of the notebook directory:

toml
# jupytext.toml at the root of your notebook directory
formats = "ipynb,py:percent"

There is also a command line interface. The README lists four operations:

bash
jupytext --set-formats ipynb,py:percent notebook.ipynb
jupytext --sync notebook.py
jupytext --to ipynb notebook.py
jupytext --pipe black notebook.ipynb

--set-formats pairs a notebook. --sync synchronizes paired files, and the README states that inputs are loaded from the most recent paired file. --to converts between formats, with -o if you want a specific output file. --pipe sends the notebook to a linter, with black as the example. After --to ipynb notebook.py, expect a notebook.ipynb next to the script.

Where Jupytext is the wrong tool

Text notebooks do not carry outputs. The README says this plainly: outputs are lost when the notebook is closed, because only inputs are saved. If your published artifact is a notebook with rendered charts and tables, a .py or .md notebook alone will not reproduce it. Paired notebooks restore the outputs, but only by keeping the .ipynb file around, which is the file you were trying to avoid diffing in the first place.

The sync rule is another sharp edge. --sync loads inputs from the most recent paired file. That is a timestamp-based rule, and it means an edit made in the older file can be overwritten rather than merged. The README does not document a conflict-resolution mode for a diverged pair, so the safe practice is to edit one side at a time.

Finally, Jupytext is a format and synchronization layer, not a notebook execution service. It does not schedule runs, cache results or turn notebooks into pipelines. If that is what you need, Jupytext is one piece of the setup rather than the whole answer.

Jupytext and nbconvert: conversion is not the same job

The most common comparison is with nbconvert, and the difference is in what each tool is for. nbconvert converts a notebook to another output, such as HTML, a script or a PDF. It is a one-way rendering step. Jupytext converts between notebook encodings that are meant to be edited and converted back, and it adds the pairing relationship and the synchronization command on top.

That distinction shows up in the CLI. jupytext --to ipynb notebook.py produces a notebook you can keep working in. jupytext --sync notebook.py reconciles two files that are supposed to represent the same notebook. nbconvert has no equivalent of the second operation, and Jupytext does not aim at the presentation formats that nbconvert produces.

The practical consequence: if you only need to export a notebook for a report, nbconvert is the smaller tool. If you need the notebook to live in Git as text and still open as a notebook, that is the problem Jupytext was built for. The two can sit in the same pipeline without overlapping.

Maintenance, releases and what the MIT licence means here

The repository is not archived. The last push was on 2026-09-21, one day before the date used for this assessment, so the project is being worked on. Releases are frequent: v1.19.5 on 2026-07-21, v1.19.4 on 2026-06-21 and v1.19.3 on 2026-05-17, roughly monthly. The package classifier in pyproject.toml is Development Status :: 5 - Production/Stable.

The dependency list is short and mostly Jupyter-adjacent: nbformat, markdown-it-py, mdit-py-plugins, packaging, pyyaml, and tomli on Python versions below 3.11. Python 3.9 and above are supported, through 3.14. That narrow footprint limits upgrade exposure compared with a tool that pulls in a large stack.

The licence is MIT, declared in pyproject.toml and in the LICENSE file at the repository root. MIT is permissive: it allows reuse and redistribution with the licence notice retained. This is not legal advice, and the licence text is the authority, but the practical point for a team is that MIT imposes no copyleft obligation on the notebooks you keep in your own repository. Your notebooks remain yours to license as you wish.

The upgrade cost sits in the file formats, not the code. If you adopt a paired setup, the .py or .md files in your repository are the durable artifact, and they are plain text. Moving off Jupytext later means converting those files back to .ipynb, which is what jupytext --to ipynb does. The lock-in is low, and that is worth weighing against the cost of teaching a team the pairing workflow.

Editorial conclusion

Adopt Jupytext if your notebooks live in Git and your team wants readable diffs, or if you want to edit notebooks in an IDE and run them in JupyterLab. Do not adopt it if you need outputs versioned in the same file as inputs, or if your workflow depends on notebook-only features that the text formats do not carry. Before rolling it out, verify one thing: that your Jupyter environment loads the Jupytext extension, since the README says activation shows up as a Notebook icon on .py and .md files. If that icon does not appear after a server restart, the pairing commands are not available and nothing else in the workflow will work.

Frequently asked questions

What does Jupytext do?

It represents Jupyter notebooks as plain text files, using formats such as py:percent for scripts and Markdown-based formats for documentation-oriented notebooks. It can also pair a text notebook with an .ipynb file so that edits in the script come back into Jupyter and outputs are reloaded from the notebook.

How to install Jupytext?

Install it in the Python environment you use for Jupyter, with pip install jupytext or conda install jupytext -c conda-forge, then restart your JupyterLab server. The README says Jupytext is activated when .py and .md files show a Notebook icon in JupyterLab.

How to use Jupytext?

In JupyterLab you can run Pair Notebook with percent Script from the command palette, or place a jupytext.toml file with formats = "ipynb,py:percent" at the root of your notebook directory. From the command line, jupytext --set-formats pairs a notebook, jupytext --sync synchronizes a pair, and jupytext --to converts between formats.

How to use Jupytext in VS Code?

The README does not document a VS Code integration. It describes opening text notebooks as notebooks with a right click in JupyterLab, and editing them in an IDE as ordinary .py or .md files. The repository does contain a demo/vscode/ directory, so the project tracks that editor, but the README gives no setup steps for it.

What is the difference between Jupytext and nbconvert?

nbconvert converts a notebook to another output such as HTML or a script, as a one-way step. Jupytext converts between notebook encodings that are meant to be edited and converted back, and adds pairing plus the jupytext --sync command for keeping two files in step.

Official sources

  1. jupytext/jupytext on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jupytext-jupytext.svg)](https://hysenlabs.com/projects/jupytext-jupytext)