Page.Store and shortcodes

Hi there,

Although I’ve been using Hugo for various projects for some time now, this is my first post here (which means the documentation is excellent!).

In one of my current projects, I’ve got a shortcode to build an audio player and I’d like to collect some information about the (possibly many) players on the page to be able to initialize them in js at the end of the page.

PAGE.Store seems like the way to do it - the doc mentions “The Store method is often used to set values within a shortcode template, a partial template called by a shortcode template, or by a render hook template.”

So I wanted to access the current page using the global page method, but the doc says “Do not use the global page function in: - shortcodes”.

I can see why caching would prevent doing so, but still I’m not sure… Is it possible to collect data from shortcodes at the page level or is it not?

If it’s possible I’d be delighted to know how - and if not, maybe the PAGE.Store doc should be rephrased a little bit to emphasize that it’s not possible?

Thanks for your help!

We refer to that as a function, not a method.

Use the Page method within a shortcode template to access the Page object. For example:

{{ .Page.Store.Set "greeting" "Hello" }}

Thanks for your quick answer and the wording clarification!

Oh for some reason I missed the shortcode Page method…

So this will work even if the shortcode has been cached? Can I be sure the information will be added to the page’s store every time?

Also, when building a list page, it seems that .Page refers to the child page directly containing the shortcode, not the parent page actually being built. Is there a way to store information in the “top” level of the list page?

Yes. Try it:

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

See https://gohugo.io/methods/page/parent/.

But you need to make sure the child pages are rendered before accessing the store from the list page.
See https://gohugo.io/troubleshooting/faq/#why-is-my-page-store-missing-a-value.

Yes. Try it:

Wow thanks for the first-class answer, with full working example! Incredible!

Out of curiosity, how does it work? Is the shortcode caching mechanism bypassed when .Page is accessed?

This page suggests that it shouldn’t work:

Hugo caches rendered shortcodes. If you use the global page function within a shortcode, and the page content is rendered in two or more templates, the cached shortcode may be incorrect.

(I know we are not talking about the global page function, but the caching mechanism should still be in the way with the .Page method)

I don’t think this solves my problem. I want to access the currently built page from the shortcode.

In your example, if I modify layouts/section.html to contain

{{ define "main" }}
<h1>{{ .Title }}</h1>
{{ .Content }}
{{ range .Pages }}
<h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
{{ .Content }}
{{ end }}
<pre>
	{{ jsonify (dict "indent" " ") (.Store.Get "foos") }}
</pre>
{{ end }}

Then /s1 displays nil at the bottom.

If you imagine "foos" is used to initialize some js functionnality, then it would not work on that page.

I could modify the shortcode to use .Page.Parent.Store but then it would not work in the single pages (/s1/pX).

just to clarify:

  • the “current” build page when in a shortcode template is the page containing the shortcode.
  • the parent is then the logical parent in the path - not necessarily the page from the range loop (although maybe in your use case it is)

instead of trying to store the stuff one level up, you could collect the info after the page has been rendered in your list page.

section.html

{{ define "main" }}
<h1>{{ .Title }}</h1>
{{ .Content }}
{{ range $page := .Pages }}
  <h2><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></h2>
  {{ .Content }}
  {{ with .Store.Get "foos" }}
    {{ $.Store.SetInMap "foos" $page.Path . }}
  {{ end }}

{{ end }}
<pre>
	{{ jsonify (dict "indent" " ") (.Store.Get "foos") }}
</pre>
{{ end }}

Thanks @irkode for the idea.

So if I get it right, there’s no way to get the “currently built” page from a shortcode, but it’s possible to “proxy” the information explicitly to “calling” pages.

Sticking together all these bits of information, I think I can do what I need. Many thanks @jmooring and @irkode for your valuable help!