HUGO
News Docs Themes Community GitHub

Passthrough render hooks

Create a passthrough render hook to override the rendering of text snippets captured by the Goldmark passthrough extension.
New in v0.132.0

Overview

Hugo uses Goldmark to render Markdown to HTML. Goldmark supports custom extensions to extend its core functionality. The Goldmark passthrough extension captures and preserves raw Markdown within delimited snippets of text, including the delimiters themselves. These are known as passthrough elements.

Depending on your choice of delimiters, Hugo will classify a passthrough element as either block or inline. Consider this contrived example:

content/sample.md
This is a

\[block\]

passthrough element with opening and closing block delimiters.

This is an \(inline\) passthrough element with opening and closing inline delimiters.

Update your site configuration to enable the passthrough extension and define opening and closing delimiters for each passthrough element type, either block or inline. For example:

markup:
  goldmark:
    extensions:
      passthrough:
        delimiters:
          block:
          - - \[
            - \]
          - - $$
            - $$
          inline:
          - - \(
            - \)
        enable: true
[markup]
  [markup.goldmark]
    [markup.goldmark.extensions]
      [markup.goldmark.extensions.passthrough]
        enable = true
        [markup.goldmark.extensions.passthrough.delimiters]
          block = [['\[', '\]'], ['$$', '$$']]
          inline = [['\(', '\)']]
{
   "markup": {
      "goldmark": {
         "extensions": {
            "passthrough": {
               "delimiters": {
                  "block": [
                     [
                        "\\[",
                        "\\]"
                     ],
                     [
                        "$$",
                        "$$"
                     ]
                  ],
                  "inline": [
                     [
                        "\\(",
                        "\\)"
                     ]
                  ]
               },
               "enable": true
            }
         }
      }
   }
}

In the example above there are two sets of block delimiters. You may use either one in your Markdown.

The Goldmark passthrough extension is often used in conjunction with the MathJax or KaTeX display engine to render mathematical expressions written in the LaTeX markup language.

To enable custom rendering of passthrough elements, create a passthrough render hook.

Context

Passthrough render hook templates receive the following context:

Attributes

(map) The Markdown attributes, available if you configure your site as follows:

markup:
  goldmark:
    parser:
      attribute:
        block: true
[markup]
  [markup.goldmark]
    [markup.goldmark.parser]
      [markup.goldmark.parser.attribute]
        block = true
{
   "markup": {
      "goldmark": {
         "parser": {
            "attribute": {
               "block": true
            }
         }
      }
   }
}

Hugo populates the Attributes map for block passthrough elements. Markdown attributes are not applicable to inline elements.

Inner

(string) The inner content of the passthrough element, excluding the delimiters.

Ordinal

(int) The zero-based ordinal of the passthrough element on the page.

Page

(page) A reference to the current page.

PageInner

(page) A reference to a page nested via the RenderShortcodes method. See details.

Position

(string) The position of the passthrough element within the page content.

Type

(string) The passthrough element type, either block or inline.

Example

Instead of client-side JavaScript rendering of mathematical markup using MathJax or KaTeX, create a passthrough render hook which calls the transform.ToMath function.

layouts/_default/_markup/render-passthrough.html
{{- $opts := dict "output" "htmlAndMathml" "displayMode" (eq .Type "block") }}
{{- with try (transform.ToMath .Inner $opts) }}
  {{- with .Err }}
    {{ errorf "Unable to render mathematical markup to HTML using the transform.ToMath function. The KaTeX display engine threw the following error: %s: see %s." . $.Position }}
  {{- else }}
    {{- .Value }}
    {{- $.Page.Store.Set "hasMath" true }}
  {{- end }}
{{- end -}}

Then, in your base template, conditionally include the KaTeX CSS within the head element:

layouts/_default/baseof.html
<head>
  {{ $noop := .WordCount }}
  {{ if .Page.Store.Get "hasMath" }}
    <link href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css" rel="stylesheet">
  {{ end }}
</head>

In the above, note the use of a noop statement to force content rendering before we check the value of hasMath with the Store.Get method.

Although you can use one template with conditional logic as shown above, you can also create separate templates for each Type of passthrough element:

layouts/
└── _default/
    └── _markup/
        ├── render-passthrough-block.html
        └── render-passthrough-inline.html

PageInner details

New in v0.125.0

The primary use case for PageInner is to resolve links and page resources relative to an included Page. For example, create an “include” shortcode to compose a page from multiple content files, while preserving a global context for footnotes and the table of contents:

layouts/shortcodes/include.html
{{ with .Get 0 }}
  {{ with $.Page.GetPage . }}
    {{- .RenderShortcodes }}
  {{ else }}
    {{ errorf "The %q shortcode was unable to find %q. See %s" $.Name . $.Position }}
  {{ end }}
{{ else }}
  {{ errorf "The %q shortcode requires a positional parameter indicating the logical path of the file to include. See %s" .Name .Position }}
{{ end }}

Then call the shortcode in your Markdown:

content/posts/p1.md
{{% include "/posts/p2" %}}

Any render hook triggered while rendering /posts/p2 will get:

  • /posts/p1 when calling Page
  • /posts/p2 when calling PageInner

PageInner falls back to the value of Page if not relevant, and always returns a value.

The PageInner method is only relevant for shortcodes that invoke the RenderShortcodes method, and you must call the shortcode using Markdown notation.

As a practical example, Hugo’s embedded link and image render hooks use the PageInner method to resolve markdown link and image destinations. See the source code for each: