Repository: gohugoio/hugo (2026-04-07T15:58:44Z)

Issue:
Don't apply nested page context handling to standalone .RenderShortcodes
The current tracking of nested page context (typically used for inclusion of content and having any relative links resolve correctly) assume that we're rendering content (either via `Page.Content` or `Page.RenderString`).

This means that the test below fails (the content never gets rendered to HTML, so the wrapper strings gets left behind).

See #12457 for a bigger discussion about this, but one use case for the below would be output format that just outputs ... Markdown.

And we should be able to detect and handle this scenario, as we already do similar checks.

```go

func TestRenderShortcodesStandalone(t *testing.T) {
	t.Parallel()

	files := `
-- hugo.toml --
disableKinds = ['section','rss','sitemap','taxonomy','term']
-- layouts/all.html --
RenderShortcodes: {{ .RenderShortcodes }}|
-- layouts/_shortcodes/headings.html --
## Heading 1
### Heading 2
{{ $p := site.GetPage "includeme" }}
RenderShortcodes2: {{ $p.RenderShortcodes }}|
-- content/p1.md --
title: "p1"
---
{{% headings "headings" %}}
-- content/includeme.md --
---
title: "includeme"
---
## Heading 2
`

	b := Test(t, files)

	b.AssertNoRenderShortcodesArtifacts()
}

```


Discussion:

Patch:
diff --git a/hugolib/page__content.go b/hugolib/page__content.go
index 7377936f309..be1f5f39671 100644
--- a/hugolib/page__content.go
+++ b/hugolib/page__content.go
@@ -1109,6 +1109,10 @@ func (c *cachedContentScope) RenderShortcodes(ctx context.Context) (template.HTM
 		return template.HTML(hugocontext.Wrap(cc, pco.po.p.pid)), nil
 	}
 
+	// Strip any Hugo context markers from nested RenderShortcodes calls
+	// that won't be processed by Goldmark.
+	cc = hugocontext.Strip(cc)
+
 	return helpers.BytesToHTML(cc), nil
 }
 
diff --git a/markup/goldmark/hugocontext/hugocontext.go b/markup/goldmark/hugocontext/hugocontext.go
index 8afe64b7ff8..5f6b5eea521 100644
--- a/markup/goldmark/hugocontext/hugocontext.go
+++ b/markup/goldmark/hugocontext/hugocontext.go
@@ -104,6 +104,14 @@ var (
 	hugoCtxRe           = regexp.MustCompile(`{{__hugo_ctx( pid=\d+)?/?}}\n?`)
 )
 
+// Strip strips any Hugo context markers from b.
+func Strip(b []byte) []byte {
+	if !bytes.Contains(b, hugoCtxPrefix) {
+		return b
+	}
+	return hugoCtxRe.ReplaceAll(b, nil)
+}
+
 var _ parser.InlineParser = (*hugoContextParser)(nil)
 
 type hugoContextParser struct{}


Test patch:
diff --git a/hugolib/integrationtest_builder.go b/hugolib/integrationtest_builder.go
index d9f05990306..ee9919a75c4 100644
--- a/hugolib/integrationtest_builder.go
+++ b/hugolib/integrationtest_builder.go
@@ -2,7 +2,6 @@ package hugolib
 
 import (
 	"bytes"
-	"context"
 	"encoding/base64"
 	"errors"
 	"fmt"
@@ -39,8 +38,8 @@ import (
 	"github.com/gohugoio/hugo/hugofs/hglob"
 	"github.com/gohugoio/hugo/hugolib/sitesmatrix"
 	"github.com/gohugoio/hugo/identity"
+	"github.com/gohugoio/hugo/media"
 	"github.com/spf13/afero"
-	"github.com/spf13/cast"
 	"golang.org/x/text/unicode/norm"
 	"golang.org/x/tools/txtar"
 )
@@ -462,12 +461,20 @@ func (s *IntegrationTestBuilder) AssertFileContentExact(filename string, matches
 
 func (s *IntegrationTestBuilder) AssertNoRenderShortcodesArtifacts() {
 	s.Helper()
-	for _, p := range s.H.Pages() {
-		content, err := p.Content(context.Background())
+	afero.Walk(s.fs.PublishDir, "", func(path string, info os.FileInfo, err error) error {
+		if err != nil || info.IsDir() {
+			return err
+		}
+		ext := strings.TrimPrefix(filepath.Ext(path), ".")
+		if !s.H.Conf.GetConfigSection("mediaTypes").(media.Types).IsTextSuffix(ext) {
+			return nil
+		}
+		content, err := afero.ReadFile(s.fs.PublishDir, path)
 		s.Assert(err, qt.IsNil)
-		comment := qt.Commentf("Page: %s\n%s", p.Path(), content)
-		s.Assert(strings.Contains(cast.ToString(content), "__hugo_ctx"), qt.IsFalse, comment)
-	}
+		comment := qt.Commentf("File: %s\n%s", path, string(content))
+		s.Assert(strings.Contains(string(content), "__hugo_ctx"), qt.IsFalse, comment)
+		return nil
+	})
 }
 
 type IntegrationTestImageHelper struct {
diff --git a/hugolib/rendershortcodes_test.go b/hugolib/rendershortcodes_test.go
index e952e258f4a..6ef7dfdeff9 100644
--- a/hugolib/rendershortcodes_test.go
+++ b/hugolib/rendershortcodes_test.go
@@ -521,3 +521,34 @@ not emphasized
 		"<p>a</p>\n<p><em>emphasized</em></p>\n<p>not emphasized</p>\n<p>b</p>\n",
 	)
 }
+
+// Issue 14732.
+func TestRenderShortcodesStandalone(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['section','rss','sitemap','taxonomy','term']
+-- layouts/all.html --
+RenderShortcodes: {{ .RenderShortcodes }}|
+-- layouts/_shortcodes/headings.html --
+## Heading 1
+### Heading 2
+{{ $p := site.GetPage "includeme" }}
+RenderShortcodes2: {{ $p.RenderShortcodes }}|
+-- content/p1.md --
+---
+title: "p1"
+---
+{{% headings "headings" %}}
+-- content/includeme.md --
+---
+title: "includeme"
+---
+## Heading 2
+`
+
+	b := Test(t, files)
+
+	b.AssertNoRenderShortcodesArtifacts()
+}