Shortcodes within shortcodes renders linebreaks as <p>

After updating to Hugo 0.113.0, shortcodes within shortcodes do not render correctly but instead are shown as text.

Example (simplified):

{{% tabs %}}
  {{% tab %}}
    {{% img src="/images/dataset-excel.png" alt="dataset-excel.png" %}}
  {{% /tab %}}
{{% /tabs %}}

outer shortcode “tabs”:

<div class='tabs'>
    <ul class="nav nav-tabs"></ul>
    <div class="tab-content">{{ .Inner }}</div>
</div>

middle shortcode template “tab”:

<div class="tab-pane" title="test">
{{ .Inner }}
</div>

Inner shortcode template “img” (simplified just to highlight the issue)

<img height="{{ $height }}" width="{{ $width }}" alt="{{ $alt }}" 
src="{{ $src }}" 
{{ if (not $noZoom) }} 
onClick="zoomImage({{ $src }})" 
{{ else }} 
class="no-zoom" 
{{ end }} 
/>

This is rendered as:

<p><img height="100" width="100" alt="dataset-excel.png" src="/images/dataset-excel.png" class="no-zoom" </p>

When I remove all linebreaks within the html-code of the img-template it renders correctly, but makes the code hard to maintain since the actual shortcode we use is much bigger than the simplified example. I already tried many things like adding markdownify in multiple places and also adding {{$_hugo_config:={ "version": 1 } }} in the shortcode templates. The weird thing is that all shortcodes render, only that the generated html code of the last shortcode is interpreted as text.

You have 3 shortcodes (tabs, tab, img), but only provide code for two of them.

Thanks for looking at my problem! I added the content of the outer most shortcode to my description, for convienience I also post it here:

shortcode “tabs”

<div class='tabs'>
    <ul class="nav nav-tabs"></ul>
    <div class="tab-content">{{ .Inner }}</div>
</div>

I also forgot to mention that the img-shortcode works perfectly fine if it is not within the {{% tabs %}}{{% tab %}}.

Which version were you using prior to updating? I’m guessing it was something very old.

Sadly we didn’t notice the problem for some time as it is only used on a sub-page. It might have worked with Hugo 0.98, for certain it worked with the very old v0.68.3

site config

[markup.goldmark.renderer]
unsafe = true

It’s not unsafe if you control the content.

markdown

{{< tabs >}}
  {{% tab %}}
  This is some **bold** text.

  {{< img src="/images/dataset-excel.png" alt="dataset-excel.png" >}}
  {{% /tab %}}
{{< /tabs >}}

layouts/shortcodes/tabs.html

<div class='tabs'>
  <div class="tab-content">
    {{ .Inner }}
  </div>
</div>

layouts/shortcodes/tab.html

<div class="tab-pane" title="test">
  {{ .Inner  }}
</div>

layouts/shortcodes/img.html

{{- $src := .Get "src" }}
{{- $height := or (.Get "height") 200 }}
{{- $width := or (.Get "width") 300 }}
{{- $alt := or (.Get "alt") "" }}
{{- $noZoom := or (.Get "noZoom") true -}}

<img
  height="{{ $height }}"
  width="{{ $width }}"
  alt="{{ $alt }}"
  src="{{ $src }}"
  {{- if (not $noZoom) }}
    onClick="zoomImage({{ $src }})"
  {{- else }}
    class="no-zoom"
  {{- end -}}
>
{{- /* keep this comment... it chomps the trailing new line */ -}}

Try it:

git clone --single-branch -b hugo-forum-topic-47869 https://github.com/jmooring/hugo-testing hugo-forum-topic-47869
cd hugo-forum-topic-47869
hugo server

Comments

The change in behavior from whatever version you were running before is due to changes made in v0.55.0 and v0.60.0.

Thanks, your solution definitely works, though I already have unsafe = true set in the config. I exchanged my code step by step with yours and found that my img-shortcode is the problem, since it works fine if I replace it with yours. (cute cat btw!)

Here is the full version of my img-shortcode that only works if I remove all line-breaks:

{{ $src := .Get "src" }}
{{ $image := resources.Get $src }}
{{ $alt := .Get "alt" }}
{{ $width := .Get "width" }}
{{ $height := .Get "height" }}
{{ $noZoom := .Get "no-zoom" }}
{{ if not $alt  }}
{{ $alt = "" }}
{{ end }}
<div>
{{ if and (not $width) (not $height)  }}
    {{ if ($image) }}
        {{ $thumb := $image.Fit "1000x400 webp q100" }}
        {{ $thumbTablet := $image.Fit "800x400 webp q100" }}
        {{ $thumbMobile := $image.Fit "500x400 webp" }}
<img height="{{ $thumb.Height }}"
        width="{{ $thumb.Width }}"
        style="max-width: min(1000px, 100%);object-fit: contain;height: fit-content;"
        alt="{{ $alt }}"
        srcset="{{$thumb.RelPermalink}} 1000w,
            {{$thumbTablet.RelPermalink}} 800w,
            {{$thumbMobile.RelPermalink}} 500w"
        sizes="100vw"
        src="{{ $thumbMobile.RelPermalink }}"
        original-src="{{ $image.RelPermalink }}"
        {{ if (not $noZoom) }}
        onClick="zoomImage(this.getAttribute('original-src'))"
        {{ else }}
        class="no-zoom"
        {{ end }}
>
    {{ else }}
<img style="max-width: 100%; max-height: 400px;" alt="{{ $alt }}" src="{{ $src }}" onClick="zoomImage({{ $src }})">
    {{ end }}
{{ else }}
    {{ if ($image) }}
<img height="{{ $height }}" width="{{ $width }}" alt="{{ $alt }}" src="{{ $image.RelPermalink }}" original-src="{{ $image.RelPermalink }}" 
        {{ if (not $noZoom) }}
        onClick="zoomImage(this.getAttribute('original-src'))"
        {{ else }}
        class="no-zoom"
        {{ end }}
>
    {{ else }}
<img height="{{ $height }}" width="{{ $width }}" alt="{{ $alt }}" src="{{ $src }}" 
        {{ if (not $noZoom) }}
        onClick="zoomImage({{ $src }})"
        {{ else }}
        class="no-zoom"
        {{ end }}>
    {{ end }}
{{ end }}
</div>

I haven’t tested your example, but I am suspicious of the indentation. Any markdown indented by four or more spaces is treated as a code block.

I will probably have time to look at this in another couple of hours.

Thank you so much! I removed all indentation to test it and it still doesn’t look correct. The version that works is this, but it makes it quire hard to maintain the code:

{{ $src := .Get "src" }}
{{ $image := resources.Get $src }}
{{ $alt := .Get "alt" }}
{{ $width := .Get "width" }}
{{ $height := .Get "height" }}
{{ $noZoom := .Get "no-zoom" }}
{{ if not $alt  }}
{{ $alt = "" }}
{{ end }}
{{ if and (not $width) (not $height)  }}
    {{ if ($image) }}
        {{ $thumb := $image.Fit "1000x400 webp q100" }}
        {{ $thumbTablet := $image.Fit "800x400 webp q100" }}
        {{ $thumbMobile := $image.Fit "500x400 webp" }}
<img height="{{ $thumb.Height }}" width="{{ $thumb.Width }}" style="max-width: min(1000px, 100%);object-fit: contain;height: fit-content;" alt="{{ $alt }}" srcset="{{$thumb.RelPermalink}} 1000w, {{$thumbTablet.RelPermalink}} 800w, {{$thumbMobile.RelPermalink}} 500w" sizes="100vw" src="{{ $thumbMobile.RelPermalink }}" original-src="{{ $image.RelPermalink }}" {{ if (not $noZoom) }}onClick="zoomImage(this.getAttribute('original-src'))" {{ else }} class="no-zoom" {{ end }} />
    {{ else }}
<img style="max-width: 100%; max-height: 400px;" alt="{{ $alt }}" src="{{ $src }}" onClick="zoomImage({{ $src }})"/>
    {{ end }}
{{ else }}
    {{ if ($image) }}
<img height="{{ $height }}" width="{{ $width }}" alt="{{ $alt }}" src="{{ $image.RelPermalink }}" original-src="{{ $image.RelPermalink }}" {{ if (not $noZoom) }} onClick="zoomImage(this.getAttribute('original-src'))" {{ else }} class="no-zoom" {{ end }}/>
    {{ else }}
<img height="{{ $height }}" width="{{ $width }}" alt="{{ $alt }}" src="{{ $src }}" {{ if (not $noZoom) }} onClick="zoomImage({{ $src }})" {{ else }} class="no-zoom" {{ end }} />
    {{ end }}
{{ end }}

I replaced the img shortcode in the test repo/branch with what you provided in this comment:
https://discourse.gohugo.io/t/shortcodes-within-shortcodes-renders-linebreaks-as-p/47869/7

It works fine. Pull changes, or clone again…

git clone --single-branch -b hugo-forum-topic-47869 https://github.com/jmooring/hugo-testing hugo-forum-topic-47869
cd hugo-forum-topic-47869
hugo server

For me it looks like this

This is my output for hugo version:

hugo v0.113.0+extended darwin/arm64 BuildDate=unknown

What does your markdown look like? Have you indented something by four or more spaces, or a tab?

This is on your branch without any changes:

On branch hugo-forum-topic-47869
Your branch is up to date with 'origin/hugo-forum-topic-47869'.

nothing to commit, working tree clean

If I change your _index.md to this, the top image renders, the second looks like in the screenshot:

{{< tabs >}}
  {{< img src="/images/dataset-excel.png" alt="dataset-excel.png" >}}
  {{% tab %}}
  This is some **bold** text.

  {{< img src="/images/dataset-excel.png" alt="dataset-excel.png" >}}
  {{% /tab %}}
{{< /tabs >}}

You are correct; it does not work fine. I made a mistake when testing.

I fixed your shortcode; please pull changes and test again. Here’s a diff:

layouts/shortcodes/img.html
diff --git a/layouts/shortcodes/img.html b/layouts/shortcodes/img.html
index eb892ddff..734a45646 100644
--- a/layouts/shortcodes/img.html
+++ b/layouts/shortcodes/img.html
@@ -23,27 +23,25 @@
         sizes="100vw"
         src="{{ $thumbMobile.RelPermalink }}"
         original-src="{{ $image.RelPermalink }}"
-        {{ if (not $noZoom) }}
+        {{- if (not $noZoom) }}
         onClick="zoomImage(this.getAttribute('original-src'))"
         {{ else }}
         class="no-zoom"
-        {{ end }}
->
+        {{ end }}>
     {{ else }}
 <img style="max-width: 100%; max-height: 400px;" alt="{{ $alt }}" src="{{ $src }}" onClick="zoomImage({{ $src }})">
     {{ end }}
 {{ else }}
     {{ if ($image) }}
 <img height="{{ $height }}" width="{{ $width }}" alt="{{ $alt }}" src="{{ $image.RelPermalink }}" original-src="{{ $image.RelPermalink }}"
-        {{ if (not $noZoom) }}
+        {{- if (not $noZoom) }}
         onClick="zoomImage(this.getAttribute('original-src'))"
         {{ else }}
         class="no-zoom"
-        {{ end }}
->
+        {{ end }}>
     {{ else }}
 <img height="{{ $height }}" width="{{ $width }}" alt="{{ $alt }}" src="{{ $src }}"
-        {{ if (not $noZoom) }}
+        {{- if (not $noZoom) }}
         onClick="zoomImage({{ $src }})"
         {{ else }}
         class="no-zoom"

Mixing markdown with raw HTML is complicated; both indentation and blank lines are important.

If you look at the diff above, all we’re doing for each conditional image block is:

  1. Removing one blank line
  2. Making sure that > is not on its own line (that’s markdown syntax for a blockquote)

For shortcodes and render hooks, I usually start every line with {{- so that I don’t have to think about it (much), and I make sure to end the template with either -}} or {{- /**/ -}} to chomp the trailing new line. This is important for inline elements such as images and links to prevent unwanted spacing between adjacent elements.

Thank you so much for your effort and the thorough explanation. Sadly for me the image is still not displayed within the {{% tab %}}. It works when I use it like {{< tab >}}, but other markdown code is not rendered. With which hugo version are you testing it on your side? This is the only difference we could have now.

Just to make sure we’re testing the same thing…
Last commit: 9096955798185a58a64da576a130f691f116aee6

I cannot reproduce what you’re seeing with v0.116.0 or later.

In v0.116.0, we updated the markdown renderer (Goldmark) from v1.5.4 to v1.5.5, which fixed https://github.com/yuin/goldmark/issues/406, which I’m pretty sure is the bug you’re seeing.

So, use Hugo v0.116.0 or later, preferably the latest release, v0.121.2.

Thank you! Updating to v0.121.2 indeed fixed the problem for me.

Thanks again for all the quick and good support!

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.