Library / SDK
jupyterlab-contrib/jupyterlab_code_formatter avatar
jupyterlab-contrib/jupyterlab_code_formatter

jupyterlab_code_formatter runs black and isort on a notebook cell

A JupyterLab plugin to facilitate invocation of code formatters.

907 stars71 forksPythonMIT

At a glance

What is it?
This JupyterLab extension adds a formatter action to the notebook interface by invoking external formatters such as isort and black from the server extension. Installing it is a two step operation, because the formatters themselves are separate packages, and a JupyterLab restart is required after the install.
Who is it for?
Adopt jupyterlab_code_formatter if your team edits notebooks in JupyterLab and wants cell formatting to behave like a keybinding instead of a terminal command, and if you are willing to pin and maintain the formatter packages on the environment side. Do not adopt it for non-notebook Python files, for which the plain formatter command line already does the job, and do not assume the extension carries a formatter with it, because the formatter is a separate install.
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 79 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 October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A toolbar action that calls a formatter you already have

Notebooks and formatting tools have never fitted together neatly. A formatter like black or isort operates on a file, while a notebook cell is a fragment of a file that happens to sit in a browser interface. Without a bridge, the only ways to format a cell are to copy it out to a file, run the tool, and paste it back, or to run the formatter over the whole notebook on disk and reload it.

jupyterlab_code_formatter is that bridge. It is described as a JupyterLab plugin to facilitate invocation of code formatters, and its repository topics list autopep8, black, yapf and R alongside the general JupyterLab extension topics. The mechanism is not a reimplementation of any formatter. The extension is configured to invoke isort and black by default for Python, which means it is a dispatcher that finds an installed formatter, hands it the code, and writes the result back into the editor.

The audience is the working data scientist or analyst who keeps a notebook as the primary artefact of their work. The value is removing a copy and paste cycle from a task that happens many times a day, in an environment where the code is already open in front of you. The cost is one more moving part in a JupyterLab deployment, and that moving part has to be installed on every environment where it is wanted.

Two packages ship together: a server plugin and a lab extension

The README explains the split explicitly. The extension is composed of a Python package named jupyterlab_code_formatter for the server extension and a NPM package named jupyterlab-code-formatter for the frontend extension. The two names differ only in the separator, which is the standard JupyterLab convention, and the troubleshooting instructions are written around that split.

The server side is the part that matters most for security and for deployment, because it is the part that executes the formatter process. The frontend side is the part that adds the menu item or button to the notebook interface. The repository layout reflects the split: a src/ directory holds the TypeScript frontend sources, a schema/ directory holds the JSON schema files that describe the settings, a jupyter-config/ directory holds the server extension configuration, and the package.json file lists schema/*.json among the published files.

Because the server extension is what runs commands on the machine hosting the notebook server, an administrator deploying this on a shared JupyterHub has to think about it as code execution in the environment of the server, not as a browser-side utility. The README does not document any sandboxing or allow list of formatters, so the set of formatters that can be invoked is whatever is installed in the environment.

Installing means adding the extension and the formatters separately

The quick start in the README is four steps, and the second one is the one people miss. First the package:

bash
pip install jupyterlab-code-formatter

Second, the formatters themselves, since the extension is configured to invoke isort and black by default for Python:

bash
# NOTE: Install black and isort,
#       JL code formatter is configured to invoke isort and black by default
pip install black isort

Third, restart JupyterLab. The README is explicit that this plugin includes a server plugin, and that you should restart JupyterLab if you followed the steps while it was already running. Skipping this is the most common reason a fresh install appears to do nothing, because the running server has not loaded the new server extension.

Fourth, configuration, which the README points at a dedicated configuration page in the hosted documentation rather than describing inline. The documentation for the project is hosted on ReadTheDocs, and the README recommends it over its own quick start, which is a reasonable signal about how thin the in-repository instructions are. The quick start above works, but the configuration page is where the list of supported formatters and their options are described.

Two commands tell you which half of the extension is missing

The troubleshooting section offers a diagnostic that maps directly onto the two package structure, and it is worth quoting because it turns a vague failure into a specific one. If you can see the frontend extension but it does nothing, check that the server extension is enabled:

bash
jupyter server extension list

If the server extension is installed and enabled but the frontend extension is not visible, check the frontend extension with:

bash
jupyter labextension list

Each command answers one question. The first tells you whether the Python side is loaded into the running server. The second tells you whether the browser side was installed into the JupyterLab frontend assets. Since the two can be installed at different times or come from different environments, running both is the fastest way to find out which half is missing.

The failure modes are not exotic. A JupyterLab 4 deployment will only pick up a server extension that was present at start, and the README's instruction to restart exists precisely because of that. A container image built once and restarted many times can also end up with a frontend asset from one install and a server package from another if the two were upgraded separately.

Configuration decides which formatter runs for which language

The setting surface is visible in the repository rather than described in prose. The package.json files list includes schema/*.json, and the repository root has a schema/ directory and a jupyter-config/ directory. The pyproject.toml makes the same point indirectly by listing the extension among the Prebuilt extensions, which means the settings are declared as a schema and the user interface is generated from it.

The consequence is that the set of formatters and the way they are invoked are data, not code paths. The README says to configure which and how formatters are invoked through the configuration page, and the dependency lists in pyproject.toml show the range the project works against. The dev and test dependency groups include autopep8, black, isort, ruff, yapf and blue, alongside rpy2:

toml
dev = [
    "autopep8",
    "black==22.1.0",
    "blue==0.9.1",
    "click==8.0.2",
    "isort",
    "jupyterlab>=4",
    "rpy2",
    "ruff",
    "yapf",
]

Two things are worth noting in that list. Several formatters are pinned to exact versions while isort and ruff are not, which tells you the project treats certain formatter versions as behaviour defining. And the presence of rpy2 indicates an R code path exists, which matches the R topic on the repository. In practice this means the plugin is not Python-only, and an organisation that wants a single formatting policy across several languages has to configure each one separately.

Building from source needs Node tooling and a pinned JupyterLab range

The published package is not a hand written wheel. The build configuration in pyproject.toml declares a build backend that pulls in a Node toolchain:

toml
[build-system]
requires = ["hatchling>=1.5.0", "jupyterlab>=4.0.0,<5", "hatch-nodejs-version>=0.3.2"]
build-backend = "hatchling.build"

The version field itself comes from the Node side, since the pyproject.toml marks version as dynamic and points the hatch version source at nodejs. The Python side requires 3.10 or above, declared as requires-python = ">=3.10", and the classifiers list support for Python 3.10 through 3.14.

The frontend build is driven by the scripts in package.json, where jlpm is the JupyterLab package manager wrapper:

json
        "build": "jlpm build:lib && jlpm build:labextension:dev",
        "build:labextension": "jupyter labextension build .",

The separation is deliberate and useful for contributors. The TypeScript library is compiled with tsc, the lab extension is packaged with the jupyter labextension build command, and there is a production variant that cleans first. The repository also carries a Taskfile.yml, a dev.Dockerfile and a docker-compose.dev.yml, so a reproducible development environment is part of the project rather than something a contributor has to invent.

Release cadence is steady, and the licence metadata disagrees with itself

The maintenance record is healthy. The recent releases are v3.1.0 on 2026-08-06, v3.0.3 on 2026-02-11 and v3.0.2 on 2024-08-14, and the last push was on 2026-08-07, a day after the newest tag. The repository is not archived, and the package version in package.json matches the newest release, at 3.1.0.

What the version numbers also tell you is that this is a JupyterLab 4 extension. The build requirement names jupyterlab>=4.0.0,<5, and the classifiers list JupyterLab 4 and the Extensions Prebuilt category. A JupyterLab 3 deployment is outside the supported range, and an upgrade of the extension is coupled to an upgrade of the lab itself.

The licence is MIT. The README states it, package.json records "license": "MIT", and the LICENSE file sits in the repository root. There is a small inconsistency worth knowing about: the pyproject.toml classifiers list a BSD licence entry, License :: OSI Approved :: BSD License, alongside the MIT text in the project table. The MIT statements are the consistent ones across the README, the package manifest and the file, but if you automate licence detection from the package metadata, expect a mismatch and check the LICENSE file. There is no CLA text, so the practical implication is the standard MIT obligations rather than a contributor licensing agreement.

The project also keeps a CHANGELOG.md and a RELEASE.md at the root, and the repository is published as a PyPI package, so upgrades arrive as ordinary pip installs rather than as a manual extension copy.

The alternative is a terminal and a copy and paste

The honest alternative to this extension is not another plugin, it is the obvious habit. Take the cell, put it in a file, run black and isort on it, and paste the result back. In a script or a CI job that is often better, because it is reproducible and leaves a record.

The difference in approach is where the formatter runs and when. A terminal invocation happens outside the notebook, in a separate environment from the kernel, on a file you had to create. The extension runs from the server extension, in the same environment as the notebook server, and writes the result back into the editor you are looking at. The default pairing of isort and black is already applied in the configured order, and the same configuration file governs both the single cell and the whole notebook, with the README demo asset named format-all.gif pointing at an action that covers more than the cell under the cursor.

Where the extension loses is scope. It formats notebook cells, and the README says nothing about formatting ordinary Python files in the same project, so a repository with both notebooks and modules ends up with two formatting workflows. It also does not give you a pre-commit hook, so nothing stops a commit of unformatted code that the extension never saw. For a team that wants a single enforced style across a whole repository, a formatter run in a hook or a continuous integration job is the mechanism that covers everything, and the extension is the ergonomic addition for the notebook side of the same policy.

Editorial conclusion

Adopt jupyterlab_code_formatter if your team edits notebooks in JupyterLab and wants cell formatting to behave like a keybinding instead of a terminal command, and if you are willing to pin and maintain the formatter packages on the environment side. Do not adopt it for non-notebook Python files, for which the plain formatter command line already does the job, and do not assume the extension carries a formatter with it, because the formatter is a separate install. Before rolling it out, check that your JupyterLab version falls inside the range the package declares, pin the formatter versions you want, and read the configuration page to set which formatter runs for which language before enabling it in a shared environment.

Frequently asked questions

How do I install jupyterlab_code_formatter?

Run pip install jupyterlab-code-formatter, install at least one formatter such as black and isort, and restart JupyterLab, because the plugin includes a server plugin that is only loaded when the server starts.

Why does the format button not work after installing the extension?

The extension is two packages, a Python server extension and a NPM frontend extension, and only one of them may be present. Run jupyter server extension list to check the server side and jupyter labextension list to check the frontend side.

Which formatters does jupyterlab_code_formatter support by default?

For Python, the default configuration invokes isort and black, and the README says to install them yourself. The development and test dependency lists also cover autopep8, ruff, yapf and blue.

Which JupyterLab versions does jupyterlab_code_formatter support?

The build requirement in pyproject.toml names jupyterlab>=4.0.0,<5, and the classifiers list JupyterLab 4. The extension targets JupyterLab 4 and above but below version 5.

What licence is jupyterlab_code_formatter released under?

The MIT licence, according to the README, the package.json manifest and the LICENSE file in the repository root. The pyproject.toml classifiers list a BSD entry, so check the LICENSE file rather than the package metadata alone.

Official sources

  1. jupyterlab-contrib/jupyterlab_code_formatter 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/jupyterlab-contrib-jupyterlab-code-formatter.svg)](https://hysenlabs.com/projects/jupyterlab-contrib-jupyterlab-code-formatter)

Was this useful?