Could shortcodes and partials be unified?

Hugo is great, and I’m trying to use it in more and more places, but I’m finding this business of shortcodes vs partial templates to be quite limiting. Both of these constructs are fundamentally trying to do the same thing: take some data and render it according to a template. The differences seem to be:

  • where they’re called from
    • shortcodes can only be called from content, partials from other templates
    • this seems like a product of history rather than design
  • what arguments you can pass them
  • whether or not they accept “inner content”
    • shortcodes let you name inner content with {{ .Inner }}
    • templates don’t do this natively, but ACE templates do have a notion of base an inner templates…

An example: I can create an image shortcode for use within Markdown content, but I can’t call that shortcode from a template that renders the same content’s front matter. For that, I need an image template! I suppose I might be able to craft the shortcode in terms of the template, but it seems a bit silly to distinguish between these things. What I really want is the expressive power of shortcodes made available to all of my partials.

So: are there reasons I just don’t understand why we can’t unify shortcodes and partial templates, or would it just be a matter of too much work and too few hands?

3 Likes

The difference isn’t only historical. It’s considerably different on the implementation.

Shortcodes are actually pulled out from the content directly, rendered separately and joined back with the content. It’s 100% Hugo’s own creation.

Partials is just an extension of the go templates native include function that searches multiple paths for the template file.

I think you make a good argument here and it’s worth looking into adding support for shortcodes to the templates, it’s not as simple as what you are outlining and would be considerable work due to how the template engine in go is parsed.

2 Likes

I think that a potential workaround would be the ability to create dictionaries on the fly, e.g.:

in my top-level template:

{{ $x = DICT_SYNTAX(Page=$, Menu=$.Site.Menus.main) }}
{{ partial "nav-bar" $x }}

in the nav-bar.ace template:

ul
  {{ range .Menu }}
    li
      {{ $current = $.Page.IsMenuCurrent(.) }}
      a href="{{ .URL }}" {{ if $current }} class="active" {{ end }}
        {{ with .Children }}
          ul
            {{ $y = DICT_SYNTAX(Page = $.Page, Menu = .) }}
            {{ partial "nav-bar" $y }}
        {{ end }}
  {{ end }}

where I’ve chosen the ugliest syntax for a dictionary that I can think of just to illustrate the concept. :slight_smile:

Note that the scratch mechanism, as currently implemented, can’t help me here: since the Scratch object is attached to the page rather than the menu, recursive evaluations of the nav-bar template would overwrite the common scratch. If, however, my ugly DICT_SYNTAX() created an initialized hugolib.Scratch object, that might just about work…

Saw this thread looking for something else, but wanted to let you know that Hugo 0.15 now has a dict template function. See the docs for more info.

1 Like

Touching in 2020 to breath some life into this comment. I was about to write a similar post. The usages of shortcodes vs partials is problematic and unifying them so that the arbitrary limitations of which types can call which and how parameters are managed would be a ‘good thing’. For backward compatibility, maybe a new construct is required which can resolve to shortcodes or partials automagically as a fallback if necessary.

2 Likes

When I feel the impulse to unify partials and shortcodes, I build the functionality in a partial, then have the shortcode call the partial.

3 Likes

Since shortcodes is a Hugo-only thing, it could be modified to parse its arguments and convert them into a dict, then invoke a partial of the same name with that dict (using normal partial lookup rules). One wrinkle: Hugo should add a first element to the dict to pass the page itself, e.g: (dict "page" . <arg1> <val1> ... )

You wouldn’t code a pass-through shortcode, just the partial. The partial would have to be coded to expect a dict (which is the way all the cool kids are doing it these days anyway).

This could be added to Hugo as the fall-through case when shortcode lookup can’t find the named file, so it would even be backward compatible.

2 Likes

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