# \[ANN\] CommonMark.jl

**URL:** <https://discourse.julialang.org/t/ann-commonmark-jl/39807>\
**Category:** Package Announcements\
**Created:** [May 20, 2020, 7:37am UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807 "2020-05-20T07:37:30Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![mike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike/32/39_2.png) [@mike](https://discourse.julialang.org/u/mike)\
**Post date:** [May 20, 2020, 7:37am UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/1 "2020-05-20T07:37:30Z")

</div>

[CommonMark.jl](https://github.com/MichaelHatherly/CommonMark.jl) is a markdown parser which is fully compliant with the [CommonMark 0.29 spec](https://spec.commonmark.org/current/). In addition to the standard features it includes several extensions such as inline and display maths, admonitions, footnotes, and tables. Additional extensions are planned, as well as a public interface for 3rd-party extensions.

The package is registered in General so can be installed with

```julia
pkg> add CommonMark

```

Please report any problems you come across in the issue tracker. Saying that, the package does pass all tests required by the CommonMark spec and so can be considered usable in the regard. Feature requests are welcome as well.

In comparison to the built-in markdown parser shipped with Julia this package provides inline HTML, correct handling of lazy paragraph continuations, and [link reference definitions](https://spec.commonmark.org/0.29/#link-reference-definitions). There may be others as well, but those are the main ones.

---

<div class="post-metadata">

**Author:** ![cormullion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cormullion/32/49131_2.png) [@cormullion](https://discourse.julialang.org/u/cormullion)\
**Post date:** [May 20, 2020, 7:43am UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/2 "2020-05-20T07:43:30Z")

</div>

Welcome back!

---

<div class="post-metadata">

**Author:** ![mike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike/32/39_2.png) [@mike](https://discourse.julialang.org/u/mike)\
**Post date:** [May 20, 2020, 8:49am UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/3 "2020-05-20T08:49:22Z")

</div>

Thanks @cormullion!

---

<div class="post-metadata">

**Author:** ![tlienart](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tlienart/32/7640_2.png) [@tlienart](https://discourse.julialang.org/u/tlienart)\
**Post date:** [May 20, 2020, 9:39am UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/4 "2020-05-20T09:39:56Z")

</div>

Awesome 👍 I look forward to replacing my crappy hacks in Franklin to work around the base Markdown parser & use your package 🙂

---

<div class="post-metadata">

**Author:** ![kevbonham](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kevbonham/32/216165_2.png) [@kevbonham](https://discourse.julialang.org/u/kevbonham)\
**Post date:** [May 20, 2020, 11:12am UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/5 "2020-05-20T11:12:04Z")

</div>

This is awesome! Does Documenter have the ability to swap out markdown parsers?

---

<div class="post-metadata">

**Author:** ![mike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike/32/39_2.png) [@mike](https://discourse.julialang.org/u/mike)\
**Post date:** [May 20, 2020, 11:16am UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/6 "2020-05-20T11:16:58Z")

</div>

That’s some heroic hacking you’ve managed in Franklin! I’d be happy to discuss merging your syntax extensions into the package, most of them shouldn’t be too difficult to achieve.

---

<div class="post-metadata">

**Author:** ![mike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike/32/39_2.png) [@mike](https://discourse.julialang.org/u/mike)\
**Post date:** [May 20, 2020, 11:25am UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/7 "2020-05-20T11:25:06Z")

</div>

Not currently, Documenter reaches into some internals of the current Markdown parser so it’s not exactly straightforward to switch out right now. I’d hope that eventually we could move towards using it ecosystem-wide since it matches the behavior of other markdown dialects a little bit better, but that will require some stress testing to make sure it’s up to the job first.

---

<div class="post-metadata">

**Author:** ![mortenpi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mortenpi/32/158_2.png) [@mortenpi](https://discourse.julialang.org/u/mortenpi)\
**Post date:** [May 20, 2020, 12:18pm UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/8 "2020-05-20T12:18:24Z")

</div>

I actually have [a branch](https://github.com/JuliaDocs/Documenter.jl/issues/1074#issuecomment-515477974) that allows for swapping out the parser. The only requirement is that your custom parser produces the `Markdown` standard library AST.

On a related note, Documenter has the [`Markdown2` module](https://github.com/JuliaDocs/Documenter.jl/blob/master/src/Utilities/Markdown2.jl), which aims to be a cleaner & stricter version of the standard library Markdown AST. I wonder if it would make sense to have a common lightweight interface package for the AST that both parsers and consumers could depend on?

---

<div class="post-metadata">

**Author:** ![StefanKarpinski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stefankarpinski/32/24_2.png) [@StefanKarpinski](https://discourse.julialang.org/u/StefanKarpinski)\
**Post date:** [May 20, 2020, 1:20pm UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/9 "2020-05-20T13:20:31Z")

</div>

This is great! It would be very good to replace the internal markdown parser with a snapshot of this external one. Or maybe ship with a version of your markdown parser but allow it to be overridden (instead baking it into the system image).

---

<div class="post-metadata">

**Author:** ![mike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike/32/39_2.png) [@mike](https://discourse.julialang.org/u/mike)\
**Post date:** [May 20, 2020, 1:56pm UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/10 "2020-05-20T13:56:53Z")

</div>

> common lightweight interface package for the AST that both parsers and consumers could depend on?

The AST used to represent the parsed documents could probably be factored out I’d think.

---

<div class="post-metadata">

**Author:** ![mike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike/32/39_2.png) [@mike](https://discourse.julialang.org/u/mike)\
**Post date:** [May 20, 2020, 1:59pm UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/11 "2020-05-20T13:59:31Z")

</div>

Thanks @StefanKarpinski.

> It would be very good to replace the internal markdown parser with a snapshot of this external one. Or maybe ship with a version of your markdown parser but allow it to be overridden (instead baking it into the system image).

I assume in a similar way to how `Pkg` is loaded these days?

---

<div class="post-metadata">

**Author:** ![StefanKarpinski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stefankarpinski/32/24_2.png) [@StefanKarpinski](https://discourse.julialang.org/u/StefanKarpinski)\
**Post date:** [May 20, 2020, 2:07pm UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/12 "2020-05-20T14:07:44Z")

</div>

Pkg is baked in, I’m afraid. We don’t really have a good model for how to do this yet. Needs to be figured out soon though.

---

<div class="post-metadata">

**Author:** ![kevbonham](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kevbonham/32/216165_2.png) [@kevbonham](https://discourse.julialang.org/u/kevbonham)\
**Post date:** [May 21, 2020, 12:23am UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/13 "2020-05-21T00:23:33Z")

</div>

To be honest, as frustrating as it can be for certain extensions of markdown to not work, I’ve come to appreciate the rigorous adherence to the standard in Base. I probably wouldn’t complain if something more permissive replaced it necessarily, but if the ecosystem (especially Documenter and Franklin) allows me to plug in whatever parser I want, that’s a better solution.

---

<div class="post-metadata">

**Author:** ![mike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike/32/39_2.png) [@mike](https://discourse.julialang.org/u/mike)\
**Post date:** [May 26, 2020, 11:07am UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/14 "2020-05-26T11:07:46Z")

</div>

Version `0.2.0` has now been registered. Release notes can be found [here](https://github.com/MichaelHatherly/CommonMark.jl/commit/9b2c4c8e2b51f947ff78a64909f1ff9e710a2d18#commitcomment-39442290) which summarize the major changes since `0.1.0`. The [README.md](https://github.com/MichaelHatherly/CommonMark.jl/blob/master/README.md) has a more complete overview of the currently available features.

* * *

An aside: the new Pkg and surrounding infrastructure is great! Big thanks to everyone involved in those efforts.

---

<div class="post-metadata">

**Author:** ![mike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike/32/39_2.png) [@mike](https://discourse.julialang.org/u/mike)\
**Post date:** [June 2, 2020, 4:05pm UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/15 "2020-06-02T16:05:58Z")

</div>

Version `0.3.0` has now been registered. A smaller release compared to `0.2.0` including:

- a [raw literals](https://github.com/MichaelHatherly/CommonMark.jl#raw-content) extension for passing through arbitrary text,
- `markdown` output (could be used to auto-format markdown documents),
- Jupyter `notebook` output (no evaluation of code cells is performed),
- and some fixes for LaTeX output.

---

<div class="post-metadata">

**Author:** ![mike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike/32/39_2.png) [@mike](https://discourse.julialang.org/u/mike)\
**Post date:** [June 13, 2020, 11:29am UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/16 "2020-06-13T11:29:13Z")

</div>

Version `0.4.0` is out. Quite a large release, with the following new features:

- [Attribute](https://github.com/MichaelHatherly/CommonMark.jl#attributes) extension for attaching metadata to AST nodes.
- [Template](https://github.com/MichaelHatherly/CommonMark.jl#writer-configuration) system for writing stand alone documents.
- [Citation and reference](https://github.com/MichaelHatherly/CommonMark.jl#citations) extension.

---

<div class="post-metadata">

**Author:** ![mike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike/32/39_2.png) [@mike](https://discourse.julialang.org/u/mike)\
**Post date:** [July 3, 2020, 1:28pm UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/17 "2020-07-03T13:28:18Z")

</div>

Version `0.5.0` is out. New features include:

- `AutoIdentifierRule` extension for Pandoc-style automatic IDs for headings.
- Allow passing a `Parser` to `open` to parse files directly.
- Better round-tripping for `markdown` writer.
- Non-strict column alignment in tables.

---

<div class="post-metadata">

**Author:** ![mike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike/32/39_2.png) [@mike](https://discourse.julialang.org/u/mike)\
**Post date:** [March 14, 2021, 5:04pm UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/18 "2021-03-14T17:04:05Z")

</div>

It’s been a while since any new features got added to the package, but recently interpolation support, along with a `@cm_str` macro, were added which is probably worth announcing here:

```julia
using CommonMark
word = "Interpolation"
cm"***$(uppercase(word))!***"

```

This was one of the last remaining missing features when comparing the package to the `Markdown` standard library.

---

<div class="post-metadata">

**Author:** ![mike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike/32/39_2.png) [@mike](https://discourse.julialang.org/u/mike)\
**Post date:** [January 8, 2026, 9:56pm UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/19 "2026-01-08T21:56:45Z")

</div>

**Version 0.10.0** is now available. It’s been a fair while since the previous release. This is likely the last 0.x release before 1.0.0.

A couple of new extensions have been added, we’re starting to run out of useful ones to add at this point:

- `StrikethroughRule` (` ~~text~~ `), `SubscriptRule` (`~text~`), `SuperscriptRule` (`^text^`)
- `TaskListRule` for `- []`/`- [x]` checkboxes
- `GitHubAlertRule` for `> [!NOTE]` style alerts
- `FencedDivRule` for Pandoc-style `::: class` blocks with nesting support
- `MarkRule` for `==highlighted==` text
- `ReferenceLinkRule` to preserve reference-style links in the AST, plus `UnresolvedReference` for detecting undefined refs

`CommonMark.Node` is now part of the public API with programmatic constructors `Node(Type, children...)` for all container types which allows for constructing complete document ASTs. Tree manipulation functions are also public: `append_child`, `prepend_child`, `insert_after`, `insert_before`, `unlink`, and `isnull`. These mutation functions have been part of the package since its initial creation and haven’t really changed, so worth having them officially available now.

A new `typst` writer joins `html`, `latex`, `markdown`, `notebook`, and `term`. We also now have Pandoc interop via a `json(ast)` writer for export and `Node(dict)` for importing directly into CommonMark’s AST representation. These match Pandoc’s JSON AST format. The `Node` constructor can also be used to convert from stdlib `Markdown` AST with `Node(md::Markdown.MD)`.

The `markdown` writer is now properly roundtrippable after several long-standing bugs were fixed. This can be used as a markdown formatter, though without any kind of configuration of the styling of the output.

Writers now accept a `transform` keyword for intercepting AST nodes during rendering, which can be used for any number of things, such as highlighting code, rewriting links, or wrapping document content in user-provided templates. It replaces a previously semi-private API for doing those kinds of modifications, which was always a bit of a hack.

Finally, documentation has moved from the lengthy README to a real Documenter.jl site: [https://michaelhatherly.github.io/CommonMark.jl/](https://michaelhatherly.github.io/CommonMark.jl/)

---

<div class="post-metadata">

**Author:** ![mike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mike/32/39_2.png) [@mike](https://discourse.julialang.org/u/mike)\
**Post date:** [February 12, 2026, 1:06pm UTC](https://discourse.julialang.org/t/ann-commonmark-jl/39807/20 "2026-02-12T13:06:58Z")

</div>

CommonMark.jl is now (finally) at 1.0.0, there’s been a couple of additions since 0.10, namely:

- [`GridTableRule`](https://michaelhatherly.github.io/CommonMark.jl/stable/extensions/#Grid-Tables) — Pandoc-style grid tables with colspan, rowspan, headers, footers, multiline cells that can contain arbitrary markdown content, for example another gridtable if you really want to.
- [`ShortcodeRule`](https://michaelhatherly.github.io/CommonMark.jl/stable/extensions/#Shortcodes) — Quarto-style shortcodes with configurable delimiters and user-defined handlers
- [`DefinitionListRule`](https://michaelhatherly.github.io/CommonMark.jl/stable/extensions/#Definition-Lists) — Pandoc-compatible definition lists with tight/loose rendering
- `markdown` roundtrip fidelity improvements related to dollar-math syntax and raw html blocks, ideally this feature should work as a markdown formatter for those that want that feature.
- an experimental [`@docstring_parser`](https://michaelhatherly.github.io/CommonMark.jl/stable/extensions/#Docstring-Parser) macro for CommonMark-formatted module docstrings, which, along with bidirectional `MarkdownAST.jl` conversion, allows using `CommonMark` as the parser for docstrings that you include in Documenter builds (with a [small hack](https://github.com/MichaelHatherly/CommonMark.jl/blob/master/docs/make.jl#L3-L25)).
