Code block render hooks

Overview

Hugo introduced Markdown render hooks in v0.62.0. When rendering Markdown to HTML, render hooks override the conversion. Each render hook is a template, with one template for each element type: heading, image, or link.

layouts/
└── _default/
    └── _markup/
        ├── render-heading.html
        ├── render-image.html
        └── render-link.html

Hugo introduced render hooks for fenced code blocks in v0.93.0.

This Markdown example contains a fenced code block:

```bash {class="my-class" id="my-codeblock" lineNos=inline tabWidth=2}
declare a=1
echo "$a"
exit
```

A fenced code block consists of:

In the previous example, the info string contains:

  • The language of the code sample (the first word)
  • An optional space-delimited or comma-delimited list of attributes (everything within braces)

Attributes

The info string may contain two types of attributes:

  1. Generic attributes
  2. Highlighting options

Generic attributes

Generic attributes can be anything. In the previous example, the generic attributes are class and id.

In the absence of special handling within a code block render hook, Hugo adds each generic attribute to the HTML element surrounding the rendered code block.

Consistent with its content security model, Hugo removes HTML event attributes such as onclick and onmouseover.

Highlighting options

Hugo uses the Chroma syntax highlighter to render the code sample. Chroma performs lexical analysis with a lexer that is unique to each code language.

You can control the appearance of the rendered code by specifying one or more highlighting options. The available options are anchorLineNos, guessSyntax, hl_Lines, lineAnchors, lineNos, lineNoStart, lineNumbersInTable, noClasses, style, and tabWidth.

When used in an info string, the names of the highlighting options are case-insensitive.

In the previous example, the highlighting options are lineNos and tabWidth.

Note that style is also a global HTML attribute. When specified in a code block info string, style is a highlighting option, not an HTML attribute.

Template

Create the code block render hook in the layouts/_default/_markup directory:

layouts/
└── _default/
    └── _markup/
        ├── render-codeblock.html
        ├── render-heading.html
        ├── render-image.html
        └── render-link.html

Context

The code block render hook receives the following context:

.Attributes
(map) The generic attributes from the info string.
.Inner
(string) The content between the leading and trailing code fences, excluding the info string.
.Options
(map) The highlighting options from the info string.
.Ordinal
(int) The zero-based ordinal of the code block on the page.
.Page
(page) A reference to the page containing the code block.
.Position
(text.Position) The position of the code block within the page content, including the filename, line number, and column number. Intended for error reporting, this can be expensive to calculate.
.Type
(string) The first word of the info string.

Functions

Hugo provides three template functions related to syntax highlighting:

transform.CanHighlight TYPE
(bool) Returns true if the syntax highlighter has a lexer for TYPE.
transform.Highlight INNER TYPE [OPTIONS]
(template.HTML) Returns highlighted code wrapped in <div>, <pre>, and <code> elements.
transform.HighlightCodeBlock CONTEXT [OPTIONS]
(highlight.HightlightResult) Returns a structure with two methods:
.Wrapped
(template.HTML) Returns highlighted code wrapped in <div>, <pre>, and <code> elements. This is identical to the value returned by the transform.Highlight function.
.Inner
(template.HTML) Returns highlighted code without any wrapping elements, allowing you to create your own wrapper.

Examples

These examples explain how to use context and functions within a code block render hook.

Default

This template replicates the behavior obtained when a code block render hook does not exist. We pass the entire context received by the template to the transform.HighlightCodeBlock function without overriding any options, then display the wrapped results.

{{ $result := transform.HighlightCodeBlock . }}
{{ $result.Wrapped }}

To prevent undesirable whitespace when displayed in the browser, Hugo renders the HTML on a single line.

<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><!-- highlighted code --></code></pre></div>

Custom wrapper

To customize the wrapper elements, use the .Wrapped or .Inner methods on thetransform.HighlightCodeBlock result.

When using the .Inner method on transform.HighlightCodeBlock

To prevent undesirable whitespace when displayed in the browser, end each line in your template with a comment that chomps the whitespace. This syntax makes the code somewhat easier to read by allowing indentation and newlines.

This syntax is necessary due to the behavior of the pre element.

This example overrides default rendering, wrapping each code block in a div element with a unique id attribute.

{{ $id := printf "codeblock-%d" .Ordinal }}
<div id="{{ $id }}">
  {{ $result := transform.HighlightCodeBlock . }}
  {{ $result.Wrapped }}
</div>

Conditional behavior

This example enables line numbers if the syntax highlighter has a lexer for the given code language (excluding plain text and Markdown) and disables line numbers for other code languages.

{{ $lineNumbers := false }}
{{ $ignoredLanguages := slice "md" "markdown" "text" "txt" }}

{{ if and (transform.CanHighlight .Type ) (not (in $ignoredLanguages .Type)) }}
  {{ $lineNumbers = "inline" }}
{{ end }}

{{ $options := merge .Options (dict "linenos" $lineNumbers) }}
{{ $result := transform.HighlightCodeBlock . $options }}
{{ $result.Wrapped }}

Template per language

Although you can use one template with conditional logic to control the behavior on a per-language basis, you can also create language-specific templates.

layouts/_default/_markup/
├── render-codeblock.html
├── render-codeblock-gallery.html
├── render-codeblock-katex.html
├── render-codeblock-mermaid.html
└── render-codeblock-python.html

For example, to use a KaTex render hook to render a mathematical expression or equation:

```katex
$$
\frac{1}{\Gamma(s)}\int_{0}^{\infty}\frac{u^{s-1}}{e^{u}-1}\mathrm{d}u
$$
```

With this capability, you can use a code block render hook instead of a shortcode for mathematical expressions, diagrams, image galleries, and more.

See these articles for specific examples:

Last modified: