Skip to main content

Markdown View Rendering (juneau-console-views-markdown)

juneau-console-views renders data tables and juneau-console renders chrome — neither has any concept of a rendered document. The optional juneau-console-views-markdown module fills that gap: hand it a markdown string, get back an HTML fragment, the same way a ViewDef turns a list of beans into a themed table.

Because it is a separate, opt-in module, consumers that don't need markdown pay nothing — no markdown parser is dragged into juneau-rest-server or the views toolkit by default.

<dependency>
<groupId>org.apache.juneau</groupId>
<artifactId>juneau-console-views-markdown</artifactId>
<version>${juneau.version}</version>
</dependency>

The MarkdownRenderer SPI​

The module's single extension point is the library-neutral MarkdownRenderer interface — one method, String toHtml(String markdown), so any parser can sit behind it:

MarkdownRenderer renderer = MarkdownRenderer.resolve();
String html = renderer.toHtml("# Runbook\n\nStep one.");

MarkdownRenderer.resolve() selects the active renderer via the standard java.util.ServiceLoader mechanism:

  • If a consumer registers their own provider (a META-INF/services/org.apache.juneau.console.views.markdown.MarkdownRenderer file naming their implementation class), that provider wins.
  • Otherwise the built-in commonmark-java default is used.

The default is intentionally not registered as a service, so a single consumer-supplied provider is always unambiguous.

The default: commonmark-java with GFM tables​

The shipped default, CommonmarkMarkdownRenderer, wraps commonmark-java (BSD-2-Clause) with the GFM tables extension enabled out of the box. GFM tables are on by default because real documents (runbooks, onboarding guides) routinely use pipe tables, and core CommonMark renders them as paragraphs of literal pipes.

CommonmarkMarkdownRenderer renderer = new CommonmarkMarkdownRenderer();

// A GFM pipe table renders as a real <table>, not literal pipes.
String html = renderer.toHtml("""
| Name | Role |
| --- | --- |
| Alice | Admin |
""");

commonmark-java's parser and renderer are immutable and thread-safe once built, so a single renderer instance is safe to share across threads and reuse for every render.

Swapping the parser​

To use a different parser (or a differently-configured commonmark instance), implement MarkdownRenderer and register it as a service:

public class MyMarkdownRenderer implements MarkdownRenderer {
@Override
public String toHtml(String markdown) {
// ... your parser here ...
}
}

META-INF/services/org.apache.juneau.console.views.markdown.MarkdownRenderer:

com.example.MyMarkdownRenderer

MarkdownRenderer.resolve() will now return your implementation instead of the commonmark default.

Styling the output​

The rendered HTML is meant to be dropped inside a .jc-prose container so it inherits the console's prose typography (heading scale, list rhythm, pre/code, blockquote, and a static-table rule):

<div class="jc-prose">
<!-- MarkdownRenderer.resolve().toHtml(...) output here -->
</div>

See Admin-Console Chrome for the .jc-prose contract.

Panel wiring​

Wiring rendered markdown into a Tab/Subtab panel's content property is a deferred follow-on integration point, not part of this module — this module ships the renderer SPI and the commonmark-java default only.

See Also​