Customization Overview
Asciidoctor.js is highly customizable. There are many ways to tailor the output to your needs, ranging from simple document attributes to building a fully custom converter.
This page provides an overview of all available customization levels, ordered from the simplest to the most advanced. Use it to find the right approach for your use case.
Quick reference
| Level | Approach | Skills required | HTML5 only |
|---|---|---|---|
1 |
AsciiDoc |
No |
|
2 |
AsciiDoc |
Yes |
|
3 |
CSS |
Yes |
|
4 |
CSS |
Yes |
|
5 |
JavaScript, HTML |
No |
|
6 |
JavaScript |
No |
|
7 |
JavaScript, AsciiDoc |
No |
|
8 |
JavaScript, template engine |
No |
|
9 |
JavaScript, Asciidoctor AST |
No |
Level 1 — Built-in attributes
The simplest customization: toggle built-in document attributes to control the output. No programming required.
-
Change the table of contents position with
toc -
Enable section numbering with
sectnums -
Set the document language with
lang -
Control syntax highlighting with
source-highlighter
Skills required
- AsciiDoc syntax
-
You need to know how to set document attributes either in the document header or via the API’s
attributesoption.
The built-in document attributes reference lists every available attribute.
Level 2 — Use a theme (stylesheet)
(HTML5 backend only)
Apply an alternative stylesheet by pointing the stylesheet attribute at a CSS file.
The Asciidoctor project no longer maintains official themes, but community-shared themes are available online.
Via the API:
import { convertFile } from '@asciidoctor/core'
await convertFile('file.adoc', { attributes: { stylesheet: 'mytheme.css' } })
Via the CLI:
$ asciidoctor -a stylesheet=mytheme.css file.adoc
Skills required
- AsciiDoc
-
Setting document attributes via the document header, the API’s
attributesoption, or the-aflag of the CLI.
See Stylesheets for the full configuration options.
Level 3 — Override the default stylesheet
(HTML5 backend only)
The stylesheet attribute replaces the default Asciidoctor stylesheet entirely.
To keep the default styles and only override specific rules, you have two options:
-
Use CSS
@importat the top of your stylesheet to pull in the default one first, then add your overrides below. -
Use a docinfo file — a plain HTML file named
docinfo.htmlplaced next to your document — to inject a<link>tag in the<head>without writing any JavaScript.
@import "https://cdn.jsdelivr.net/npm/@asciidoctor/core/data/asciidoctor-default.css";
/* your overrides below */
h1, h2, h3 { font-family: Georgia, serif; }
The default stylesheet source is a good reference for identifying the selectors you want to change.
Skills required
- CSS
-
You need to know CSS selectors and the cascade to override the right rules without unintended side effects.
See Stylesheets for the configuration options.
Level 4 — Create your own stylesheet
(HTML5 backend only)
Build a completely custom stylesheet from scratch or use the default Asciidoctor stylesheet as a starting point.
Skills required
- CSS
-
Solid understanding of CSS is required.
See Stylesheets for the configuration options.
Level 5 — Docinfo processor
A docinfo processor injects custom content into the <head> element or at the end of the <body> of the generated document.
This is a lightweight way to add analytics scripts, custom meta tags, or extra stylesheets without touching the converter.
import asciidoctor from '@asciidoctor/core'
const registry = asciidoctor.Extensions.create()
registry.docinfoProcessor(function () {
this.atLocation('head')
this.process(function (doc) {
return '<meta name="description" content="My custom page">'
})
})
Skills required
- JavaScript
-
You write the extension as a JavaScript class or function.
- HTML
-
The processor returns a raw HTML string that is injected verbatim into the document.
Learn how to write a Docinfo Processor.
Level 6 — Postprocessor
A postprocessor receives the fully converted output as a string and returns a modified version. Use it to add a banner, strip generated content, or apply output-level transformations that are not tied to any specific node.
import asciidoctor from '@asciidoctor/core'
const registry = asciidoctor.Extensions.create()
registry.postprocessor(function () {
this.process(function (doc, output) {
return output.replace('</body>', '<footer>Custom footer</footer></body>')
})
})
Skills required
- JavaScript
-
You write the postprocessor as a JavaScript class or function.
- Output format knowledge
-
You manipulate the raw output string, so you need to understand the target format (HTML, DocBook, …) to make safe transformations.
Learn how to write a Postprocessor.
Level 7 — Custom block or macro extension
Extend the AsciiDoc syntax itself by registering a new block, block macro, or inline macro. The extension receives the parsed attributes and the raw source of the custom block, and returns converted output.
Use a block processor when you want to wrap a delimited block with custom rendering, and a macro processor when you want a call-site shorthand (e.g., video::id[]).
import asciidoctor from '@asciidoctor/core'
const registry = asciidoctor.Extensions.create()
registry.blockMacro('shoutout', function () {
this.process(function (parent, target, attrs) {
const text = `Shoutout to ${target}!`
return this.createBlock(parent, 'paragraph', text)
})
})
Skills required
- JavaScript
-
You write the extension as a JavaScript class or function.
- AsciiDoc syntax
-
You need to understand how AsciiDoc blocks and macros are defined to design a consistent syntax for your new element.
- Asciidoctor API (basic)
-
Creating and returning nodes uses the Asciidoctor content model API (
createBlock,createList, etc.).
See the Block Processor, Block Macro Processor, and Inline Macro Processor pages for complete examples.
Level 8 — Custom templates
A template converter lets you override the rendering of individual AsciiDoc elements (paragraph, section, table, listing block, …) with template files. You only need to provide templates for the elements you want to change; all other elements fall back to the built-in converter.
Supported template engines: EJS, Handlebars, Nunjucks, Pug.
Skills required
- JavaScript
-
You configure the template converter and install the template engine dependency.
- Template engine
-
You write template files in the engine of your choice. Each template receives the node object and can call the Asciidoctor API to read attributes and content.
- Asciidoctor node attributes (basic)
-
You need to know which attributes and methods are available on each node type to produce the right output.
Learn how to set up a Template Converter.
Level 9 — Custom converter
A custom converter is a JavaScript class that implements the convert(node, transform) method.
The converter is called for every node in the document tree, giving you complete, fine-grained control over the output.
This is the right choice when the target format is not HTML or when the built-in converter cannot be adapted through templates alone.
import asciidoctor from '@asciidoctor/core'
class TextConverter {
convert (node, transform) {
const nodeName = transform || node.getNodeName()
if (nodeName === 'document') return node.getContent()
if (nodeName === 'section') return `${node.getTitle()}\n${node.getContent()}`
if (nodeName === 'paragraph') return `${node.getContent()}\n`
return node.getContent ? node.getContent() : ''
}
}
asciidoctor.ConverterFactory.register(new TextConverter(), ['text'])
Skills required
- JavaScript
-
A converter is a plain JavaScript class. Comfort with object-oriented patterns helps.
- Asciidoctor AST
-
You will interact with every node type in the document tree (Document, Section, Block, List, Table, …). Understanding which methods are available on each node is essential.
- Target format
-
You produce the raw output yourself, so you need to understand the format you are generating.
Learn how to build a Custom Converter.