cmark: the C reference implementation of CommonMark with AST access and five output formats
CommonMark parsing and rendering library and program in C
At a glance
- What is it?
- cmark is the C reference library for CommonMark, a specification that resolves Markdown's parsing ambiguities with a formal grammar. It parses to an AST, renders to HTML, LaTeX, groff man, CommonMark and XML, and has no external runtime dependencies.
- Who is it for?
- cmark is the right tool for server-side Markdown rendering where C ABI compatibility matters, where performance on large documents is a requirement, or where output needs to match what commonmark.js renders in a browser. Skip it when a C build chain is unavailable, when GitHub Flavored Markdown extensions are required, or when the rendering pipeline cannot hold a full document AST in memory.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly C, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 11, 2026, and from our analysis. They are not legal advice.
Editorial analysis
CommonMark resolves Markdown parsing ambiguities; cmark is its C reference implementation
Standard Markdown has no formal grammar, and different parsers produce different output for edge cases: nested brackets, emphasis markers adjacent to punctuation, and deeply nested block quotes all behave inconsistently across implementations. CommonMark resolves those ambiguities with an explicit specification at spec.commonmark.org. cmark is the C reference implementation of that specification.
The library, libcmark, provides functions for parsing CommonMark documents to an abstract syntax tree, manipulating the tree, and rendering to five output formats: HTML, groff man, LaTeX, CommonMark itself, and an XML representation of the AST. A command-line program, also named cmark, wraps the library for direct use in shell pipelines. Both the library and the program are written in standard C99 with no external dependencies, and the project has been tested with MSVC, gcc, tcc and clang.
The JavaScript reference implementation is commonmark.js, maintained by the same organization at github.com/commonmark/commonmark.js. Since both follow the same specification, content rendered with cmark on a server and previewed with commonmark.js in a browser will produce matching output. That compatibility is an explicit stated use case.
cmake is required to build; GNU make wraps the steps into three commands
Building cmark and libcmark requires cmake. If scanners.re is modified, re2c 0.14.2 or later is also needed to regenerate scanners.c from it; a pre-generated scanners.c ships in the repository to remove that dependency for standard builds.
With GNU make, three commands cover build, test and install:
make
make test
make installThe default installation prefix is /usr/local. Passing INSTALL_PREFIX at the first make invocation changes it:
make INSTALL_PREFIX=pathFor a portable cmake workflow without GNU make, the README gives four steps:
cmake -S . -B build
cmake --build build
ctest --test-dir build
cmake --install buildThe executable lands in build/src/cmark. Passing -DCMAKE_INSTALL_PREFIX=path to the first cmake command changes the destination. On Windows with MSVC, the build uses Makefile.nmake:
nmake /f Makefile.nmakeFor cross-compiling a Windows binary on Linux with mingw32, running make mingw places the binaries in build-mingw/windows/bin.
Raw HTML and four link schemes are scrubbed by default; --unsafe removes all filters
By default, cmark scrubs raw HTML from input and filters out links with four schemes: javascript:, vbscript:, data:, and file:. That default makes it safe to pipe cmark output directly to a browser without a separate sanitization step, for any Markdown source that does not need those features.
Passing --unsafe on the command line, or setting CMARK_OPT_UNSAFE in the library API, removes all those filters at once. The project documentation advises using an HTML sanitizer specific to your needs to protect against XSS attacks if you do so. It names the attack class but specifies no particular sanitizer, leaving that choice to the caller. Raw HTML passes through verbatim when CMARK_OPT_UNSAFE is active.
The split between a safe default and an explicit opt-out is a design choice with a concrete implication. A pipeline that accepts user-submitted Markdown gets XSS protection for free. A pipeline that processes trusted content, such as an internal documentation system, can switch off the filters without changing the parsing behavior. The two use cases are deliberately separated rather than controlled by a sanitizer configuration the caller has to configure correctly. User-submitted Markdown stays behind the default filter; trusted content sources can remove it explicitly.
127ms for War and Peace on a ten-year-old laptop, and a test suite built around pathological inputs
cmark was fuzz-tested with american fuzzy lop. The test suite includes pathological inputs that cause exponential backtracking in parsers that use regular expressions or naive backtracking: specifically, thousands-deep nested bracketed text and block quotes. Vicent Marti optimized the C implementation for performance, increasing its speed tenfold from the original baseline.
Project benchmarks place cmark at 127 milliseconds for War and Peace on a ten-year-old laptop, 10,000 times faster than the original Markdown.pl, and on a par with the fastest available Markdown processors. The data behind those figures is in benchmarks.md at the repository root. The Makefile provides two benchmark targets:
make benchFor more detailed results:
make newbenchFor testing memory leaks, the Makefile provides:
make leakcheckFor a quick fuzz run over ten long randomly generated inputs:
make fuzztestFor systematic AFL fuzzing with an existing installation:
AFL_PATH=/path/to/afl_directory make aflThe FUZZCHARS variable controls input size for make fuzztest, with a default of 2000000.
The AST is the only manipulation layer; no streaming API is documented
cmark's architecture is parse-then-render. The parser produces a complete abstract syntax tree from the input, and the renderer traverses that tree to produce formatted output. No streaming API, SAX-style event interface, or mechanism for transforming documents during parsing is documented in the library.
That architecture has a practical implication: a program processing a large Markdown document must hold the full AST in memory. For most documents this poses no problem, but it is a boundary to check for environments with tight memory constraints, such as embedded systems or constrained containers.
Programmatic manipulation of the AST is the main reason to use libcmark as a library rather than the cmark command-line tool. Adding a new output format requires only format-specific output logic; the library handles traversal. Nick Wellnhofer contributed most of the C library's API and its test harness, along with many other improvements to the project, as listed in the authors section of the repository.
Language wrappers call into libcmark; commonmark.js reimplements the spec natively for the browser
libcmark has wrappers for Go, Haskell, Ruby, Lua, Perl, Python, R, Scala and PHP, each linked in the README. These are independent packages maintained by their respective authors, not by the cmark organization. Using one means parsing behavior is determined by the version of libcmark the wrapper links against, which makes the C library a transitive dependency of the wrapper's language package.
For Python, Lua and Ruby, calling libcmark directly via the language's foreign function interface is straightforward, and the wrappers/ subdirectory contains simple examples. That path avoids a third-party wrapper dependency when the FFI overhead is acceptable.
commonmark.js is a different kind of alternative. It is not a wrapper around libcmark but a native JavaScript reimplementation of the CommonMark specification, maintained by the same organization. The practical difference is deployment context: cmark runs server-side or in any C-compatible environment, while commonmark.js runs in a browser or Node.js without requiring a compiled binary. Both claim full spec conformance, and interoperability between cmark server output and commonmark.js browser rendering is a documented use case. For client-side rendering where loading a compiled C library is not an option, commonmark.js is the appropriate implementation, and using it alongside cmark on the server is the documented path to consistent rendering across environments.
0.31.2 is the current release; BSD2 is stated in the README but GitHub cannot classify the licence
0.31.2 was released on 2026-02-14. 0.31.1 was released on 2024-08-03 and 0.31.0 on 2024-01-29. The last push to the repository was on 2026-09-01. That cadence, one release in the fourteen months between 0.31.1 and 0.31.2, is consistent with a stable, mature project rather than one under active feature development.
The README lists the project as BSD2-licensed. GitHub's license detector classifies it as 'Other' and cannot identify the licence automatically. The authoritative text is in COPYING at the repository root, not in the README's one-word summary. Verify the terms in COPYING before redistributing any build. This paragraph is an observation, not legal advice.
Issues and questions about CommonMark behavior are split across two locations. General discussion goes to the CommonMark forum at talk.commonmark.org. The project asks for simple, clear, actionable issues to be filed at the GitHub issue tracker at github.com/commonmark/CommonMark/issues rather than at the cmark repository directly. A document at why-cmark-and-not-x.md in the repository root addresses when to choose cmark over other implementations.
Editorial conclusion
cmark is the right tool for server-side Markdown rendering where C ABI compatibility matters, where performance on large documents is a requirement, or where output needs to match what commonmark.js renders in a browser. Skip it when a C build chain is unavailable, when GitHub Flavored Markdown extensions are required, or when the rendering pipeline cannot hold a full document AST in memory. Before using --unsafe, choose an HTML sanitizer appropriate to the output context, since CMARK_OPT_UNSAFE removes all built-in XSS protection from the rendered output.
Frequently asked questions
What is cmark?
cmark is the C reference implementation of CommonMark, a specification for Markdown that defines parsing rules unambiguously. It provides a shared library (libcmark) and a command-line program, both written in standard C99 with no external runtime dependencies.
How do I build cmark?
Building requires cmake. With GNU make, running make, make test, and make install covers the full process. For a cmake-only workflow without GNU make, the sequence is cmake -S . -B build, cmake --build build, ctest --test-dir build, and cmake --install build.
What output formats does cmark support?
cmark renders to HTML, groff man, LaTeX, CommonMark itself, and an XML representation of the AST. The format is chosen via command-line flags or through the library API.
Is cmark safe to use with untrusted Markdown input?
By default, cmark scrubs raw HTML and filters out javascript:, vbscript:, data:, and file: link schemes. Passing --unsafe or using CMARK_OPT_UNSAFE removes those filters; the project advises using an HTML sanitizer for XSS protection in that case.
What is the difference between cmark and commonmark.js?
cmark is the C reference implementation of the CommonMark specification, running server-side or in C-compatible environments. commonmark.js is a native JavaScript reimplementation of the same specification, suited for browser or Node.js use where a compiled C library is not available.
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/commonmark-cmark)