Call shortcodes in Markdownified text?

Got an interesting use case

  • in front matter, pages have multiline values for some YAML fields that, like any other page content, have Markdown and shortcodes
  • the contents get passed into a shortcode, so that it can be formatted into an HTML structure we need (in this case tabs)
  • the shortcode for tabs has to use | markdownify in order for the Markdown to process

THE PROBLEM: the calls to other shortcodes do not get processed and render as plain text

Is there a workaround?

Front matter:

---
mytabs:
- id: tab1
  title: Option 1
  content: |
    Multiline value with {{< shortcode>}}
---
{{< tabs data="mytabs">}}

Then, inside tabs.html

<div class="tab-pane{{ if eq $i 0 }} active-pane{{ end }}" id="{{ .id }}">{{ .content | markdownify }}</div>

Almost makes me wish there was something like the old eval js function so I could force a Hugo process order, i.e. {{ .content | eval | markdownify }} – or, as someone else who had this problem suggested in this bug, | markdownifyShortcode

There is an open request for such a thing here, but what approach could replace mine in the meantime?

1 Like

There a simple workaround for simple strings.

# content/about/that-page.md
title: That page!
  blocks:
    - layout: text
      content: When reading %%Section%% you will notice...

Then you can create a partial in charge of parsing those strings

{{/* layouts/partials/func/ParseString.html */}}
{{ $content := .string }}
{{ if in $content "%%Section%%"  }}
  {{ with .Page }}
    {{ with .Parent }}
      {{ $content = replace $content "%%Section%%" .Title }}
    {{ end }}
  {{ end }}
{{ end }}
{{ if in $content "%%SomethingElse%%"  }}
[ repeat ]
{{ end }}

{{ return $content }}

And in our template:

{{/* layouts/_default/single.html */}}
{{ range .blocks }}
  <div class="text">
    {{ with partial "func/ParseString" (dict "string" .content "Page" $) }}
      {{ . | markdownify }}
    {{ end }}
  </div>
{{ end }}

This a basic example which would need some refactoring if you endup with lots of “parsable strings”.

Now if you were to do something as complex as shortcodes, you’d have to study Go regex hard and use findRe and replaceRe to identify your shortcodes, their parameters and Inner and print the desired output.

If you build that solution, I’m interested, but I’d rather Hugo handles that for us.

3 Likes

The above is a great solution unless you want to use any of the built in shortcodes. I’ve gotten around this by using headless bundles.

Each page could be and individual tab that can take content and Hugo shortcodes.

You can then call the page bundle in a template or partial like this:

{{ $content_bundle := "/tabs/index.md" }}
{{ range $i, $content_bundle.Resources.Match "*.md" }}
    <div class="tab-pane{{ if eq $i 0 }} active-pane{{ end }}" id="{{ .id }}">
        {{ .content | markdownify }}
    </div>
{{ end }}

Might take a little longer to setup, but this will be far easier to maintain than your own custom shortcode system. This will process Hugo shortcodes and any custom Hugo shortodes.

You can checkout my implementation for landing pages here: