This documentation covers a prerelease version of the software. Follow this link to view the documentation for the stable version (3.0) instead.

Syntax Highlighting

This page explains how to enable syntax highlighting for source blocks in Asciidoctor.js.

Default behavior

Without a syntax highlighter configured, Asciidoctor.js renders a source block like this:

[source,java]
....
public class Kalle {
  public Kalle(final String pelle) {}
}
....

as plain HTML with language metadata on the <code> element:

<div class="content">
  <pre class="highlight">
    <code class="language-java" data-lang="java">public class Kalle {
  public Kalle(final String pelle) {}
}</code>
  </pre>
</div>

The class and data-lang attributes are present, but there is no highlighted markup. To get visual syntax highlighting you need either a client-side library that runs in the browser, or a server-side highlighter that processes the source at conversion time.

Client-side highlighting with highlight.js

Asciidoctor.js ships with a built-in highlight.js adapter. When enabled, Asciidoctor.js injects the highlight.js stylesheet and script into the output document, and the browser applies highlighting when the page loads.

Standalone documents

To use the built-in adapter, convert with standalone: true and set the source-highlighter attribute to highlightjs (or highlight.js):

import { convert } from '@asciidoctor/core'

const content = `= Sample Java
[source,java]
....
public class Kalle {
  public Kalle(final String pelle) {}
}
....`

const html = await convert(content, {
  standalone: true, (1)
  attributes: { 'source-highlighter': 'highlightjs' }, (2)
})
1 The standalone option wraps the body in a full HTML document (<html>, <head>, <body>). The highlight.js <link> and <script> tags are injected via the docinfo mechanism, which only runs in standalone mode.
2 Tells Asciidoctor.js to use the built-in highlight.js adapter.

The generated document loads highlight.js from a CDN and calls hljs.highlightBlock() on every pre.highlight > code[data-lang] element.

If you set source-highlighter but omit standalone: true (which is the default for convert()), the docinfo scripts are not injected and highlighting will not work.

Customizing the theme

Override the default github theme by setting the highlightjs-theme attribute:

await convert(content, {
  standalone: true,
  attributes: {
    'source-highlighter': 'highlightjs',
    'highlightjs-theme': 'monokai',
  },
})

Any theme name from the highlight.js theme gallery is accepted.

Self-hosted highlight.js

To serve highlight.js from your own host instead of the CDN, set the highlightjsdir attribute to the base URL of your installation:

await convert(content, {
  standalone: true,
  attributes: {
    'source-highlighter': 'highlightjs',
    'highlightjsdir': '/assets/highlight.js',
  },
})

Asciidoctor.js will load styles/github.min.css and highlight.min.js relative to that URL.

Embedding in an existing page

If you are inserting the converted fragment into an existing HTML page (i.e. without standalone: true), you must include highlight.js yourself and initialize it after inserting the fragment:

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.3/styles/github.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.18.3/highlight.min.js"></script>
import { convert } from '@asciidoctor/core'

const html = await convert(content) // standalone is false by default

document.getElementById('content').innerHTML = html

// Highlight all source blocks that Asciidoctor.js produced
document.querySelectorAll('pre.highlight > code[data-lang]').forEach((el) => {
  hljs.highlightBlock(el)
})

Server-side (conversion-time) highlighting

Server-side highlighting processes the source at conversion time so the highlighted markup is embedded directly in the output HTML. This approach does not require any client-side JavaScript.

Asciidoctor.js does not ship a built-in server-side highlighter, but you can implement one by extending SyntaxHighlighterBase and overriding handlesHighlighting()true and highlight().

Here is a minimal example using the highlight.js npm package to highlight at conversion time:

import { load, SyntaxHighlighterBase } from '@asciidoctor/core'
import hljs from 'highlight.js' // npm install highlight.js

class HljsServerHighlighter extends SyntaxHighlighterBase {
  handlesHighlighting() {
    return true (1)
  }

  highlight(node, source, lang, opts) {
    if (lang && hljs.getLanguage(lang)) {
      return hljs.highlight(source, { language: lang }).value (2)
    }
    return hljs.highlightAuto(source).value
  }
}

const doc = await load(content, {
  safe: 'safe',
  syntax_highlighters: { 'hljs-server': HljsServerHighlighter }, (3)
  attributes: { 'source-highlighter': 'hljs-server' },
})
const html = await doc.convert()
1 Returning true tells Asciidoctor.js that this highlighter processes source at conversion time.
2 source is the raw source text; return the highlighted markup as a plain string.
3 Register the highlighter under the name used in source-highlighter.

See Custom Syntax Highlighter for the full API.