mdformat: a CommonMark formatter that refuses to guess
CommonMark compliant Markdown formatter
At a glance
- What is it?
- mdformat is a MIT licensed pure Python Markdown formatter that enforces one consistent style, checks formatting in continuous integration and can be extended with plugins for GitHub Flavored Markdown and for code block formatters. The judgement is that the opinionated, CommonMark-only design is the point, and that the backslash escaping it applies to non CommonMark engines is the cost users notice first.
- Who is it for?
- mdformat fits a team that wants one Markdown style across a repository, a pre-commit hook that keeps it that way, and a formatter that will not silently change what a document renders as, since it is CommonMark compliant and escapes anything outside that specification.
- 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 4 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 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One style, enforced rather than negotiated
mdformat is described as an opinionated Markdown formatter that can be used to enforce a consistent style in Markdown files, and it is both a Unix style command line tool and a Python library. The word opinionated does a lot of work in that sentence, because it means the project has made the decisions for you and offers no configuration surface for most of them. That is the trade at the centre of the project, fewer knobs in exchange for a repository that stops drifting, and it is why the tool is usually introduced to a team rather than chosen by one. The format it targets is CommonMark, the specification rather than any particular engine dialect, and the README is explicit that mdformat is a CommonMark formatter. The documentation lives at mdformat.readthedocs.io, and the README itself is only a quickstart for the command line, with the style guide and the plugin documentation living in the docs.
Installing with pipx, and adding GFM if you need it
The installation is one command, and pipx is the recommended route because it keeps the tool in its own environment:
pipx install mdformatIf your Markdown is GitHub Flavored Markdown rather than plain CommonMark, the README gives a second command that injects the plugin into the same environment:
pipx install mdformat
pipx inject mdformat mdformat-gfmThat two step shape is easy to misread, because the base package installs with CommonMark support only, and the dialect you actually write in is a separate concern. The same pattern applies to code block formatters, which are also plugins. The package manifest is minimal in a way that matches the philosophy, with a dependency on markdown-it-py for the parser and tomli only on Python older than 3.11, and a console script called mdformat that points at mdformat.__main__:run.
The command line: format, check, and standard input
Three invocations cover most use. Formatting files in place is mdformat with the paths, so mdformat README.md CHANGELOG.md rewrites both files. Formatting a tree recursively is mdformat with a dot. Reading Markdown from standard input until EOF and writing the result to standard output is mdformat with a single dash, which is the form that lets the tool sit in a pipeline. The check mode is the one that belongs in continuous integration:
mdformat --check README.md CHANGELOG.mdThat applies no changes, and the README is specific about the outcome, since the exit code is non-zero if a file is not properly formatted. The option list is short by design. There is --no-validate to skip checking that the rendered HTML is consistent, --number to apply consecutive numbering to ordered lists, --wrap with keep, no or an integer for paragraph word wrap, --end-of-line with lf, crlf or keep, --exclude with a Unix style glob pattern, --extensions and --codeformatters to require and enable plugins, with --no-extensions and --no-codeformatters to turn them off. One detail catches people out, since the --exclude option is only available on Python 3.13 and later.
Why your MkDocs syntax comes back with backslashes
The first FAQ entry explains the behaviour that surprises new users, and the explanation is a specification argument rather than a bug. mdformat is a CommonMark formatter and does not have out-of-the-box support for syntax beyond what the CommonMark specification defines. The custom syntax that engines such as MkDocs, Hugo, Obsidian and GitHub introduce typically redefines the meaning of angle brackets, square brackets, parentheses and the hash character, all of which are special in CommonMark, so mdformat often resorts to backslash escaping those characters to ensure its formatting changes never alter a rendered document. The escalation is that some engines, MkDocs being named, do not support CommonMark to begin with, which makes incompatibilities unavoidable rather than accidental. The README's advice is to use a plugin, and to look for support in the plugin docs or the mdformat GitHub topic, or to consider a documentation generator that adheres to CommonMark as its base syntax, with mdBook and Sphinx with Markdown given as the two suggestions.
Why not Prettier
The second FAQ entry is a direct comparison, and it deserves a full read because it explains the design rather than just the preference. mdformat is pure Python code, and Python is pre-installed on macOS and on virtually any Linux distribution, which means that typically little to no additional installation is required to run it, and the same argument holds when it is used together with pre-commit, which is also Python. Prettier on the other hand requires Node.js and npm. The second argument concerns correctness. Prettier suffers from numerous bugs, many of which cause changes in the Markdown AST and in the rendered HTML, and the README attributes much of that to Prettier's use of remark-parse v8.x as its Markdown parser, which the author of that parser describes as inferior to markdown-it, the parser mdformat uses. The README adds that remark-parse v9.x is advertised as CommonMark compliant and would presumably fix many of the issues, but that it is not used.
Plugins carry the dialect and the code formatters
The extension story is where mdformat stops being opinionated, and it is documented in a set of pages the README links to, covering plugin usage, a plugin development guide, a list of code block formatter plugins and a list of parser extension plugins. Two plugin families exist and they solve different problems. A parser extension changes how Markdown is parsed, which is how a dialect such as GitHub Flavored Markdown is supported, and a code block formatter changes how the code inside a fenced block is laid out, which is how you get a language specific formatter to run over embedded code. The command line ties them together, since --extensions and --codeformatters require and enable plugins of the matching kind, and all are enabled by default, with --no-extensions and --no-codeformatters to turn that off. The docs also cover using mdformat as a pre-commit hook, which is the usual way a team adopts it, and the repository itself ships a .pre-commit-hooks.yaml so the hook can be referenced by name rather than configured by hand.
Packaging, tests and the state of the project
The manifest describes a project that keeps its footprint small. It builds with setuptools, requires Python 3.10 or later, declares the MIT licence with the text in a LICENSE file, and lists only markdown-it-py and the conditional tomli as dependencies. The classifiers cover the console, macOS, Windows and POSIX Linux, along with the Text Processing Markup topic and Typing Typed, and the keywords name mdformat, markdown, commonmark, formatter and pre-commit, which tells you the intended integration. The tooling is modern Python practice, with isort configured to be independent of import type and to use the black profile, pytest running with strict markers and strict config, tox for environments, a bumpversion configuration file and a flake8 configuration. The repository top level also carries a fuzzer directory, which is a good sign for a parser project, plus a pre-commit configuration and a readthedocs configuration. The version in the manifest is 1.0.0, the project publishes no GitHub releases, the last push was on 2026-10-19, and the repository is not archived, so the package is distributed through PyPI while the changelog lives in the docs.
Editorial conclusion
mdformat fits a team that wants one Markdown style across a repository, a pre-commit hook that keeps it that way, and a formatter that will not silently change what a document renders as, since it is CommonMark compliant and escapes anything outside that specification. It is the wrong choice if your documentation relies on engine specific syntax such as MkDocs directives or Obsidian features, because the formatter will escape those characters and some engines such as MkDocs do not support CommonMark to begin with. It is also a poor fit if you want the wide formatting options of a general purpose formatter such as Prettier, since mdformat deliberately offers a smaller set of choices. Before you adopt it, run it in check mode across your documentation and read the diff, and install the GitHub Flavored Markdown plugin if that is the dialect your files use.
Frequently asked questions
How do I install mdformat?
Run pipx install mdformat, which installs the CommonMark compliant formatter into its own environment. If your files use GitHub Flavored Markdown, add the plugin with pipx inject mdformat mdformat-gfm.
How do I check whether Markdown files are formatted correctly?
Use mdformat --check with the files, so mdformat --check README.md CHANGELOG.md. That mode applies no changes, and the exit code is non-zero when a file is not properly formatted, which makes it the form to use in continuous integration.
Why does mdformat escape my MkDocs or Obsidian syntax with backslashes?
Because mdformat is a CommonMark formatter, and engines such as MkDocs, Hugo, Obsidian and GitHub introduce syntax that redefines characters CommonMark treats as special, so the formatter escapes them to make sure its changes never alter a rendered document. MkDocs does not support CommonMark at all, so incompatibilities are unavoidable there, and the fix is a plugin or a CommonMark based generator such as mdBook or Sphinx with Markdown.
Official sources
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.
[](https://hysenlabs.com/projects/hukkin-mdformat)Was this useful?