Java library for parsing and rendering [Markdown] text according to the [CommonMark] specification (and some extensions).
Provides classes for parsing input to an abstract syntax tree (AST), visiting and manipulating nodes, and rendering to HTML or back to Markdown. It started out as a port of [commonmark.js], but has since evolved into an extensible library with the following features:
The library is supported on Java 11 and later. It works on Android too, but that is on a best-effort basis, please report problems. For Android the minimum API level is 19, see the commonmark-android-test directory.
Coordinates for core library (see all on [Maven Central]):
<dependency>
<groupId>org.commonmark</groupId>
<artifactId>commonmark</artifactId>
<version>0.28.0</version>
</dependency>
The module names to use in Java 9 are org.commonmark,
org.commonmark.ext.autolink, etc, corresponding to package names.
Note that for 0.x releases of this library, the API is not considered stable
yet and may break between minor releases. After 1.0, [Semantic Versioning] will
be followed. A package containing beta means it's not subject to stable API
guarantees yet; but for normal usage it should not be necessary to use.
See the spec.txt
file if you're wondering which version of the spec is currently
implemented. Also check out the [CommonMark dingus] for getting familiar
with the syntax or trying out edge cases. If you clone the repository,
you can also use the DingusApp class to try out things interactively.
import org.commonmark.node.*;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
var parser = Parser.builder().build();
var document = parser.parse("This is *Markdown*");
var renderer = HtmlRenderer.builder().build();
renderer.render(document); // "
This is <em>Markdown</em>
\n"
This uses the parser and renderer with default options. Both builders have methods for configuring their behavior:
escapeHtml(true) on HtmlRenderer will escape raw HTML tags and blocks.sanitizeUrls(true) on HtmlRenderer will strip potentially unsafe URLs
from <a> and <img> tagsNote that this library doesn't try to sanitize the resulting HTML with regards to which tags are allowed, etc. That is the responsibility of the caller, and if you expose the resulting HTML, you probably want to run a sanitizer on it after this.
import org.commonmark.node.*;
import org.commonmark.renderer.markdown.MarkdownRenderer;
// Build document
var heading = new Heading();
heading.setLevel(2);
heading.appendChild(new Text("My heading"));
var document = new Document();
document.appendChild(heading);
// Render to Markdown
var renderer = MarkdownRenderer.builder().build();
renderer.render(document); // "## My heading\n"
For rendering to plain text with minimal markup, there's also TextContentRenderer.
After the source text has been parsed, the result is a tree of nodes. That tree can be modified before rendering, or just inspected without rendering:
Node node = parser.parse("Example\n=======\n\nSome more text");
WordCountVisitor visitor = new WordCountVisitor();
node.accept(visitor);
visitor.wordCount; // 4
class WordCountVisitor extends AbstractVisitor {
int wordCount = 0;
@Override
public void visit(Text text) {
// This is called for all Text nodes. Override other visit methods for other node types.
// Count words (this is just an example, don't actually do it this way for various reasons).
wordCount += text.getLiteral().split("\\W+").length;
// Descend into children (could be omitted in this case because Text nodes don't have children).
visitChildren(text);
}
}
If you want to know where a parsed Node appeared in the input source text,
you can request the parser to return source positions like this:
var parser = Parser.builder().includeSourceSpans(IncludeSourceSpans.BLOCKS_AND_INLINES).build();
Then parse nodes and inspect source positions:
var source = "foo\n\nbar *baz*";
var doc = parser.parse(source);
var emphasis = doc.getLastChild().getLastChild();
var s = emphasis.getSourceSpans().get(0);
s.getLineIndex(); // 2 (third line)
s.getColumnIndex(); // 4 (fifth column)
s.getInputIndex(); // 9 (string index 9)
s.getLength(); // 5
source.substring(s.getInputIndex(), s.getInputIndex() + s.getLength()); // "*baz*"
If you're only interested in blocks and not inlines, use IncludeSourceSpans.BLOCKS.
Sometimes you might want to customize how HTML is rendered. If all you want to do is add or change attributes on some elements, there's a simple way to do that.
In this example, we register a factory for an AttributeProvider on the
renderer to set a class="border" attribute on img elements.
Parser parser = Parser.builder().build();
HtmlRenderer renderer = HtmlRenderer.builder()
.attributeProviderFactory(new AttributeProviderFactory() {
public AttributeProvider create(AttributeProviderContext context) {
return new ImageAttributeProvider();
}
})
.build();
Node document = parser.parse("");
renderer.render(document);
// "
<img src=\"/url.png\" alt=\"text\" class=\"border\" />
\n"
class ImageAttributeProvider implements AttributeProvider {
@Override
public void setAttributes(Node node, String tagName, Map<String, String> attributes) {
if (node instanceof Image) {
attributes.put("class", "border");
}
}
}
If you want to do more than just change attributes, there's also a way to take complete control over how HTML is rendered.
In this example, we're changing the rendering of indented code blocks to
only wrap them in pre instead of pre and code:
Parser parser = Parser.builder().build();
HtmlRenderer renderer = HtmlRenderer.builder()
.nodeRendererFactory(new HtmlNodeRendererFactory() {
public NodeRenderer create(HtmlNodeRendererContext context) {
return new IndentedCodeBlockNodeRenderer(context);
}
})
.build();
Node document = parser.parse("Example:\n\n code");
renderer.render(document);
// "
Example:
\n<pre>code\n</pre>\n"
class IndentedCodeBlockNodeRenderer implements NodeRenderer {
private final HtmlWriter html;
IndentedCodeBlockNodeRenderer(HtmlNodeRendererContext context) {
this.html = context.getWriter();
}
@Override
public Set<Class<? extends Node>> getNodeTypes() {
// Return the node types we want to use this renderer for.
return Set.of(IndentedCodeBlock.class);
}
@Override
public void render(Node node) {
// We only handle one type as per getNodeTypes, so we can just cast it here.
IndentedCodeBlock codeBlock = (IndentedCodeBlock) node;
html.line();
html.tag("pre");
html.text(codeBlock.getLiteral());
html.tag("/pre");
html.line();
}
}
In case you want to store additional data in the document or have custom
elements in the resulting HTML, you can create your own subclass of
CustomNode and add instances as child nodes to existing nodes.
To define the HTML rendering for them, you can use a NodeRenderer as
explained above.
There are a few ways to extend parsing or even override built-in parsing,
all of them via methods on Parser.Builder
(see Blocks and inlines in the spec for an overview of blocks/inlines):
enabledBlockTypescustomBlockParserFactorycustomInlineContentParserFactorycustomDelimiterProcessorlinkProcessor and linkMarkerBoth the Parser and HtmlRenderer are designed so that you can
configure them once using the builders and then use them multiple
times/from multiple threads. This is done by separating the state for
parsing/rendering from the configuration.
Having said that, there might be bugs of course. If you find one, please report an issue.
Javadocs are available online on javadoc.io.
Extensions need to extend the parser, or the HTML renderer, or both. To use an extension, the builder objects can be configured with a list of extensions. Because extensions are optional, they live in separate artifacts, so additional dependencies need to be added as well.
Let's look at how to enable tables from GitHub Flavored Markdown. First, add an additional dependency (see [Maven Central] for others):
<dependency>
<groupId>org.commonmark</groupId>
<artifactId>commonmark-ext-gfm-tables</artifactId>
<version>0.28.0</version>
</dependency>
Then, configure the extension on the builders:
import org.commonmark.ext.gfm.tables.TablesExtension;
List<Extension> extensions = List.of(TablesExtension.create());
Parser parser = Parser.builder()
.extensions(extensions)
.build();
HtmlRenderer renderer = HtmlRenderer.builder()
.extensions(extensions)
.build();
To configure another extension in the above example, just add it to the list.
The following extensions are developed with this library, each in their own artifact.
Turns plain links such as URLs and email addresses into links (based on [autolink-java]).
Use class AutolinkExtension from artifact commonmark-ext-autolink.
Enables strikethrough of text by enclosing it in ~~. For example, in
hey ~~you~~, you will be rendered as strikethrough text.
Use class StrikethroughExtension in artifact commonmark-ext-gfm-strikethrough.
Enables tables using pipes as in [GitHub Flavored Markdown][gfm-tables].
Use class TablesExtension in artifact commonmark-ext-gfm-tables.
Adds support for GitHub-style alerts (also known as callouts or admonitions) as described here, e.g.:
> [!NOTE]
> The text of the note.
As types you can use NOTE, TIP, IMPORTANT, WARNING, CAUTION; or configure the extension to add additional ones.
Use class AlertsExtension in artifact commonmark-ext-gfm-alerts. See the
AlertsExtension README for more information.
Enables footnotes like in GitHub or Pandoc:
Main text[^1]
[^1]: Additional text in a footnote
Inline footnotes like ^[inline footnote] are also supported when enabled via FootnotesExtension.Builder#inlineFootnotes.
Use class FootnotesExtension in artifact commonmark-ext-footnotes.
Enables adding auto generated "id" attributes to heading tags. The "id" is based on the text of the heading.
# Heading will be rendered as:
<h1 id="heading">Heading</h1>
Use class HeadingAnchorExtension in artifact commonmark-ext-heading-anchor.
In case you want custom rendering of the heading instead, you can use
the IdGenerator class directly together with a
HtmlNodeRendererFactory (see example above).
Enables underlining of text by enclosing it in ++. For example, in
hey ++you++, you will be rendered as underline text. Uses the <ins> tag.
Use class InsExtension in artifact commonmark-ext-ins.
Adds support for metadata through a YAML front matter block. This extension only supports a subset of YAML syntax. Here's an example of what's supported:
---
key: value
list:
- value 1
- value 2
literal: |
this is literal value.
literal values 2
---
document start here
Use class `YamlFron
$ claude mcp add commonmark-java \
-- python -m otcore.mcp_server <graph>