Java library for parsing and rendering Markdown text according to the CommonMark specification
—
High-performance HTML rendering system with extensive customization options including URL sanitization, attribute providers, custom node renderers, and output formatting controls. The HTML renderer converts CommonMark AST nodes into clean, standards-compliant HTML output.
Main rendering class that converts AST nodes to HTML output.
/**
* Renders a tree of nodes to HTML.
* Thread-safe renderer that can be reused across multiple rendering operations.
*/
public class HtmlRenderer implements Renderer {
/**
* Create a new builder for configuring an HtmlRenderer
* @return a builder instance
*/
public static Builder builder();
/**
* Render the node to HTML and return as string
* @param node the node to render - must not be null
* @return the rendered HTML as a string
*/
public String render(Node node);
/**
* Render the node to HTML and append to the given output
* @param node the node to render - must not be null
* @param output the appendable to write HTML to
*/
public void render(Node node, Appendable output);
}Usage Examples:
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.node.Node;
// Basic HTML rendering
Parser parser = Parser.builder().build();
HtmlRenderer renderer = HtmlRenderer.builder().build();
Node document = parser.parse("# Hello\n\nThis is **bold** text.");
String html = renderer.render(document);
// Result: <h1>Hello</h1>\n<p>This is <strong>bold</strong> text.</p>\n
// Render to StringBuilder
StringBuilder sb = new StringBuilder();
renderer.render(document, sb);
// Render to file
try (FileWriter writer = new FileWriter("output.html")) {
renderer.render(document, writer);
}Builder class for configuring HTML renderer instances with custom options and extensions.
/**
* Builder for configuring an HtmlRenderer
*/
public static class HtmlRenderer.Builder {
/**
* Build the configured HtmlRenderer instance
* @return the configured HtmlRenderer
*/
public HtmlRenderer build();
/**
* The HTML to use for rendering a softbreak, defaults to "\n"
* Set it to "<br>" to make them hard breaks
* Set it to " " to ignore line wrapping in the source
* @param softbreak HTML for softbreak
* @return this builder
*/
public Builder softbreak(String softbreak);
/**
* Whether HtmlInline and HtmlBlock should be escaped, defaults to false
* @param escapeHtml true for escaping, false for preserving raw HTML
* @return this builder
*/
public Builder escapeHtml(boolean escapeHtml);
/**
* Whether Image src and Link href should be sanitized, defaults to false
* @param sanitizeUrls true for sanitization, false for preserving raw attribute
* @return this builder
*/
public Builder sanitizeUrls(boolean sanitizeUrls);
/**
* UrlSanitizer used to filter URLs if sanitizeUrls is true
* @param urlSanitizer Filterer used to filter Image src and Link href
* @return this builder
*/
public Builder urlSanitizer(UrlSanitizer urlSanitizer);
/**
* Whether URLs of link or images should be percent-encoded, defaults to false
* @param percentEncodeUrls true to percent-encode, false for leaving as-is
* @return this builder
*/
public Builder percentEncodeUrls(boolean percentEncodeUrls);
/**
* Add a factory for an attribute provider for adding/changing HTML attributes to the rendered tags
* @param attributeProviderFactory the attribute provider factory to add
* @return this builder
*/
public Builder attributeProviderFactory(AttributeProviderFactory attributeProviderFactory);
/**
* Add a factory for instantiating a node renderer (done when rendering)
* @param nodeRendererFactory the factory for creating a node renderer
* @return this builder
*/
public Builder nodeRendererFactory(HtmlNodeRendererFactory nodeRendererFactory);
/**
* Add extensions to use on this HTML renderer
* @param extensions extensions to use on this HTML renderer
* @return this builder
*/
public Builder extensions(Iterable<? extends Extension> extensions);
}Usage Examples:
import org.commonmark.renderer.html.HtmlRenderer;
import org.commonmark.renderer.html.AttributeProvider;
// Custom softbreak handling
HtmlRenderer brRenderer = HtmlRenderer.builder()
.softbreak("<br>\n")
.build();
// Escape HTML content
HtmlRenderer safeRenderer = HtmlRenderer.builder()
.escapeHtml(true)
.build();
// URL sanitization
HtmlRenderer sanitizedRenderer = HtmlRenderer.builder()
.sanitizeUrls(true)
.percentEncodeUrls(true)
.build();
// Custom attribute provider
HtmlRenderer customRenderer = HtmlRenderer.builder()
.attributeProviderFactory(context -> new AttributeProvider() {
@Override
public void setAttributes(Node node, String tagName, Map<String, String> attributes) {
if (node instanceof Heading) {
attributes.put("class", "heading-" + ((Heading) node).getLevel());
}
}
})
.build();Extension interfaces and classes for customizing HTML rendering behavior.
/**
* Extension interface for HTML renderer
*/
public interface HtmlRenderer.HtmlRendererExtension extends Extension {
/**
* Extend the HTML renderer builder with custom functionality
* @param rendererBuilder the builder to extend
*/
void extend(HtmlRenderer.Builder rendererBuilder);
}
/**
* Factory for HTML node renderers
*/
public interface HtmlNodeRendererFactory {
/**
* Create a node renderer with the given context
* @param context the rendering context
* @return a node renderer instance
*/
NodeRenderer create(HtmlNodeRendererContext context);
}
/**
* Context for HTML node rendering
*/
public interface HtmlNodeRendererContext {
/**
* Whether HTML should be escaped
* @return true if HTML should be escaped
*/
boolean shouldEscapeHtml();
/**
* Whether URLs should be sanitized
* @return true if URLs should be sanitized
*/
boolean shouldSanitizeUrls();
/**
* Get the URL sanitizer
* @return the URL sanitizer
*/
UrlSanitizer urlSanitizer();
/**
* Encode a URL if percent encoding is enabled
* @param url the URL to encode
* @return the encoded URL
*/
String encodeUrl(String url);
/**
* Extend attributes for a node and tag
* @param node the node being rendered
* @param tagName the HTML tag name
* @param attributes the current attributes
* @return the extended attributes
*/
Map<String, String> extendAttributes(Node node, String tagName, Map<String, String> attributes);
/**
* Get the HTML writer
* @return the HTML writer
*/
HtmlWriter getWriter();
/**
* Get the softbreak string
* @return the softbreak HTML
*/
String getSoftbreak();
/**
* Render a child node
* @param node the node to render
*/
void render(Node node);
}System for adding custom HTML attributes to rendered elements.
/**
* Provides attributes for HTML elements
*/
public interface AttributeProvider {
/**
* Set attributes for the specified node and tag
* @param node the node being rendered
* @param tagName the HTML tag name
* @param attributes the attributes map to modify
*/
void setAttributes(Node node, String tagName, Map<String, String> attributes);
}
/**
* Factory for attribute providers
*/
public interface AttributeProviderFactory {
/**
* Create an attribute provider with the given context
* @param context the attribute provider context
* @return an attribute provider instance
*/
AttributeProvider create(AttributeProviderContext context);
}
/**
* Context for attribute providers
*/
public interface AttributeProviderContext {
// Context methods for attribute provision
}Usage Examples:
import org.commonmark.renderer.html.AttributeProvider;
import org.commonmark.node.*;
// Add CSS classes to headings
AttributeProvider headingClassProvider = new AttributeProvider() {
@Override
public void setAttributes(Node node, String tagName, Map<String, String> attributes) {
if (node instanceof Heading) {
Heading heading = (Heading) node;
attributes.put("class", "heading heading-" + heading.getLevel());
}
}
};
// Add target="_blank" to external links
AttributeProvider externalLinkProvider = new AttributeProvider() {
@Override
public void setAttributes(Node node, String tagName, Map<String, String> attributes) {
if (node instanceof Link && tagName.equals("a")) {
Link link = (Link) node;
if (link.getDestination().startsWith("http")) {
attributes.put("target", "_blank");
attributes.put("rel", "noopener");
}
}
}
};
// Use providers
HtmlRenderer renderer = HtmlRenderer.builder()
.attributeProviderFactory(context -> headingClassProvider)
.attributeProviderFactory(context -> externalLinkProvider)
.build();System for sanitizing URLs in links and images for security.
/**
* Sanitizes URLs in links and images
*/
public interface UrlSanitizer {
/**
* Sanitize a link URL
* @param url the URL to sanitize
* @return the sanitized URL, or null to remove the URL
*/
String sanitizeLinkUrl(String url);
/**
* Sanitize an image URL
* @param url the URL to sanitize
* @return the sanitized URL, or null to remove the URL
*/
String sanitizeImageUrl(String url);
}
/**
* Default URL sanitizer implementation
*/
public class DefaultUrlSanitizer implements UrlSanitizer {
/**
* Sanitize a link URL - allows http, https, ftp, mailto
* @param url the URL to sanitize
* @return the sanitized URL, or null if not allowed
*/
public String sanitizeLinkUrl(String url);
/**
* Sanitize an image URL - allows http, https, data
* @param url the URL to sanitize
* @return the sanitized URL, or null if not allowed
*/
public String sanitizeImageUrl(String url);
}Usage Examples:
import org.commonmark.renderer.html.UrlSanitizer;
import org.commonmark.renderer.html.DefaultUrlSanitizer;
// Custom URL sanitizer
UrlSanitizer strictSanitizer = new UrlSanitizer() {
@Override
public String sanitizeLinkUrl(String url) {
// Only allow https URLs
if (url.startsWith("https://")) {
return url;
}
return null; // Remove unsafe URLs
}
@Override
public String sanitizeImageUrl(String url) {
// Only allow https images or data URLs
if (url.startsWith("https://") || url.startsWith("data:image/")) {
return url;
}
return null;
}
};
HtmlRenderer renderer = HtmlRenderer.builder()
.sanitizeUrls(true)
.urlSanitizer(strictSanitizer)
.build();Low-level HTML writing utility used by node renderers.
/**
* Helper for writing HTML output
*/
public class HtmlWriter {
/**
* Create an HTML writer that writes to the given appendable
* @param out the appendable to write to
*/
public HtmlWriter(Appendable out);
/**
* Write raw HTML content (not escaped)
* @param s the raw HTML to write
*/
public void raw(String s);
/**
* Write text content (HTML-escaped)
* @param text the text to write
*/
public void text(String text);
/**
* Write an HTML tag with no attributes
* @param name the tag name
*/
public void tag(String name);
/**
* Write an HTML tag with attributes
* @param name the tag name
* @param attrs the attributes map
*/
public void tag(String name, Map<String, String> attrs);
/**
* Write an HTML tag with attributes, specifying if it's a void element
* @param name the tag name
* @param attrs the attributes map
* @param voidElement true if this is a void element (like <br>)
*/
public void tag(String name, Map<String, String> attrs, boolean voidElement);
/**
* Write a line break
*/
public void line();
}The built-in renderer that handles all core CommonMark nodes.
/**
* Core HTML renderer for built-in nodes
*/
public class CoreHtmlNodeRenderer extends AbstractVisitor implements NodeRenderer {
/**
* Create a core HTML node renderer with the given context
* @param context the rendering context
*/
public CoreHtmlNodeRenderer(HtmlNodeRendererContext context);
/**
* Get the node types this renderer handles
* @return set of node types
*/
public Set<Class<? extends Node>> getNodeTypes();
/**
* Render the specified node
* @param node the node to render
*/
public void render(Node node);
// Visit methods for all core node types (inherited from AbstractVisitor)
}Usage Examples:
import org.commonmark.renderer.html.HtmlWriter;
import org.commonmark.renderer.html.HtmlNodeRendererContext;
import org.commonmark.renderer.NodeRenderer;
import org.commonmark.node.CustomBlock;
// Custom node renderer example
public class CustomBlockRenderer implements NodeRenderer {
private final HtmlNodeRendererContext context;
private final HtmlWriter html;
public CustomBlockRenderer(HtmlNodeRendererContext context) {
this.context = context;
this.html = context.getWriter();
}
@Override
public Set<Class<? extends Node>> getNodeTypes() {
return Collections.singleton(CustomBlock.class);
}
@Override
public void render(Node node) {
CustomBlock customBlock = (CustomBlock) node;
Map<String, String> attrs = new HashMap<>();
attrs.put("class", "custom-block");
html.tag("div", context.extendAttributes(node, "div", attrs));
renderChildren(customBlock);
html.tag("/div");
}
private void renderChildren(Node parent) {
Node child = parent.getFirstChild();
while (child != null) {
context.render(child);
child = child.getNext();
}
}
}
// Register custom renderer
HtmlRenderer renderer = HtmlRenderer.builder()
.nodeRendererFactory(context -> new CustomBlockRenderer(context))
.build();Install with Tessl CLI
npx tessl i tessl/maven-org-commonmark--commonmark