Leverage shortcodes in Hugo

Learn some useful tips without having to embed HTML in your markdown

Julio Pescador

I’ve been trying out Hugo recently for my website which is a great and fun tool to use. There is a particular feature that you can use which I find to be very helpful. This feature is called shortcodes . What are shortcodes exactly? Here is a brief explanation from their website:

Hugo uses Markdown for its simple content format. However, there are a lot of things that Markdown doesn’t support well. We are unwilling to accept being constrained by our simple format. Also unacceptable is writing raw HTML in our Markdown every time we want to include unsupported content such as a video…
To avoid both of these limitations, Hugo created shortcodes.
A shortcode is a simple snippet inside a content file that Hugo will render using a predefined template.

So what does this mean? What this means is you can create a method and call that method in your markdown without having to embed HTML. This will keep your markdown clean as a result. It can also save you time if you need to make an update to your shortcode. For example, your shortcode could consist of an img element that is contained in a span element with a class attribute.

<span class="image left">
    <img src="/path/to/file" alt="Some alt text" />
</span>

You call this shortcode in multiple files, say hundreds of them. You later decide that you need to update that snippet by adding another class or you no longer wish to contain the img element in a span element. That is not an issue with shortcodes because Hugo will merge in your changes. We can see why this can be useful especially if we went with the alternate option, by embedding the same HTML directly in our markdown. Searching and replacing that snippet of HTML in every file would be a hassle which is why using shortcodes is preferable.

Let’s use the span img element sample to create a shortcode. We’ll also implement some extra features in our shortcode. First, we’ll implement the shortcode using positional and named parameters. Second, we’ll have the ability to dynamically reference the path to the file by using the following methods: by date, title, or an explicit path. Finally, the image can be placed in the center, left, or right of the page.

Parameters for our shortcode

Let’s create our shortcode and call it img-post. It will consist of 4 parameters:

  • Parameter 1 will be the path
    • We will have the option to pass in the following values: date, title, or an explicit path. The date and title value will be explained in just a bit.
    • The named parameter will be called path
  • Parameter 2 will be the name of your file including the file extension
    • The named parameter will be called file
  • Parameter 3 will contain the alt text for our image
    • The named parameter will be called alt
  • Finally, parameter 4 will determine the image placement based on the following values: left, center, or center
    • The named parameter will be called type

Please note that parameters 3 and 4 can be optional. The default value will place the image in the center and leave the alt text blank. Let’s start implementing these parameters into our shortcode.

Part 1 of our shortcode

First, we’ll perform a check on the number of parameters passed into our shortcode. We’ll then determine whether the parameters passed in are positional or named. Here is the snippet of code for this part:

{{ $numOfParams := len .Params }}
{{ if and (ge $numOfParams 2) (le $numOfParams 4) }}
    <!-- Check if the Parameter is positional or named
         which can handle both -->
    {{ if .IsNamedParams }}
        {{ $.Scratch.Set "path" (.Get "path") }}
        {{ $.Scratch.Set "file" (.Get "file") }}
        {{ $.Scratch.Set "alt"  (.Get "alt") }}
        {{ $.Scratch.Set "type" (.Get "type") }}
    {{ else }}
        {{ $.Scratch.Set "path" (.Get 0) }}
        {{ $.Scratch.Set "file" (.Get 1) }}
        {{ if ge $numOfParams 3 }}
            {{ $.Scratch.Set "alt" (.Get 2) }}
        {{ else }}
            {{ $.Scratch.Set "alt" "" }}
        {{ end }}
        {{ if ge $numOfParams 4 }}
            {{ $.Scratch.Set "type"  (.Get 3) }}
        {{ else }}
            {{ $.Scratch.Set "type" "center" }}
        {{ end }}
    {{ end }}

The shortcode will check if the user has passed in at least 2 parameters and that the number of parameters isn’t greater than 4. If the check passes, it will then determine if the parameters are named by using the .IsNamedParams variable. It will then proceed to get the named parameters using .Get "nameOfParameter" if the variable is true. However, it will use .Get indexNumber if the variable is false which means the parameters are positional. Please note that indexNumber starts at index 0 for parameter 1 and the last parameter will be n-1. You can see that the code immediately retrieves the values for index 0 and 1. However, for index 2 and 3 it first checks if it can retrieve that index based on the number of positional parameters passed in. Otherwise, we’ll get an index out of bounds error if we don’t check. Let’s proceed to use our values now that we got them.

Part 2 of our shortcode

Next, we’ll determine what path we’ll use based on the value passed in. We have three options: date, title, or the explicit path. Here is what the next section of code will do:

    {{ $file := $.Scratch.Get "file" }}
    {{ $type := $.Scratch.Get "type" }}
    {{ $alt  := $.Scratch.Get "alt" }}
    {{ $path := $.Scratch.Get "path" }}

    {{ if eq $path "title" }}
        {{ $.Scratch.Set "path" "/img/" }}
        {{ $.Scratch.Add "path" (lower (replace .Page.LinkTitle " " "-")) }}
    {{ else if eq $path "date" }}
        {{ $.Scratch.Set "path" "/img/" }}
        {{ $.Scratch.Add "path" (.Page.Date.Format "2006/01") }}
    {{ else }}
        {{ $.Scratch.Set "path" $path }}
    {{ end }}
    {{ $path := $.Scratch.Get "path" }}

You can see that it first assigns the value of our scratch variables to a local variable. This part is for convenience so that we can just call {{ $variableName }} instead of {{ $.Scratch.Get "variableName" }}. Next, it will check the $path variable to see what it’s equal to. If it’s not equal to date or title, then it’ll use the explicit path that was passed into our shortcode. Let’s see what the code does when the path isn’t explicit.

The root of the path will always begin at img for date and title. The path will then be concatenated with the page .LinkTitle variable if the $path value is equal to title. The .LinkTitle variable is altered before concatenation by converting the text to lowercase and replacing any spaces with the hyphen character. Therefore, if your post is called “My First Post” then the path will be /img/my-first-post. Now, if the path value is equal to date then concatenation will also occur, but the .Date variable will be used. The variable is formatted to yyyy/mm before concatenation. Therefore, if the date of your post is March 10th, 2016 then the path will be /img/2016/03. Let’s proceed to the last part now that we have our path set.

Part 3 of our shortcode

Finally, the shortcode will create the HTML for our image and place it accordingly based on the type or position 3 parameter. Here is the last part of our code:

    {{ if eq $type "left" }}
        <span class="image left">
            <img src="{{ $path }}/{{ $file }}" alt="{{ $alt }}" />
        </span>
    {{ else if eq $type "right" }}
        <span class="image right">
            <img src="{{ $path }}/{{ $file }}" alt="{{ $alt }}" />
        </span>
    {{ else }}
        <p><span class="image center-image">
            <img src="{{ $path }}/{{ $file }}" alt="{{ $alt }}" />
        </span></p>
    {{ end }}
{{ end }}

We can see that it checks for three types based on the $type variable and creates the HTML content using the local variables we set earlier: $path, $file, and $alt. It will check the $type variable whether the image should be placed on the left, right, or anything else other than the first two. The code will therefore center the image by default if we don’t set the value to the left or right. So, you can technically pass in a blank string or not pass in a value which gives us the convenience to pass in less for more.

Our shortcode in action

Let’s go over some examples now that we’ve covered all parts of our shortcode.

Positional using the date path and placing the image to the left

{{< img-post "date" "Grumpy-Cat.jpg" "Grumpy Cat" "left" >}}

Grumpy Cat Here’s some random text and the image to the left. Let’s pad this with some extra text. Ready, go! Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut pretium arcu erat, nec condimentum sem accumsan nec. Aenean ultricies risus metus, ut pellentesque orci semper quis. In condimentum velit cursus, laoreet mauris in, fermentum erat. Morbi elementum, lacus id vestibulum consequat, mauris nisi finibus ligula, at tempus ipsum nunc vitae purus. Maecenas et odio sit amet mi scelerisque fringilla nec ut nisl. Donec nec nisi pharetra, ullamcorper est at, aliquet lorem.

Integer facilisis vitae purus at tincidunt. Phasellus sed convallis lacus, eget aliquet libero. Curabitur feugiat risus eu dui viverra, euismod gravida neque bibendum. Donec efficitur dapibus faucibus.

Positional using the title path and placing the image in the center

{{< img-post "title" "Doge.jpg" "Doge" >}}

Doge

Named parameter using an explicit path and placing the image to the right

{{< img-post
    path="/img/2016/03" file="Grumpy-Cat.jpg"
    alt="Grumpy Cat" type="right" >}}

Grumpy Cat Here’s some random text and the image to the right. Let’s pad this with some extra text. Ready, go! Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris nisl lacus, finibus id enim non, egestas placerat nisi. Mauris rhoncus sagittis egestas. Donec rhoncus sagittis risus at rutrum. Fusce turpis sem, bibendum non est non, dignissim luctus erat. Duis iaculis ultricies justo sit amet eleifend. Fusce tristique eu nisi eu aliquet. Vivamus nec orci id quam sollicitudin varius. In interdum vestibulum mauris, sed eleifend dolor posuere quis. Nam ornare hendrerit est sit amet interdum. Proin id magna vestibulum, mollis velit vitae, euismod libero. Nunc ultrices ut mi at pharetra.

Praesent eget est at orci suscipit fringilla sed sed dui. Fusce ac ipsum tortor. Curabitur nec mollis ante, at luctus elit. Vestibulum scelerisque sapien quis nibh venenatis tempor. Cras iaculis nulla.

Now back to the center with named parameters and using the date path

{{< img-post path="date" file="Dog-wink.jpg" alt="Dog Wink" >}}

Dog Wink

We can see the advantage shortcodes can provide if done correctly. You can avoid embedding HTML in your markdown so that maintenance is easier. You can place some additional logic to have it do whatever you need it to do. Finally, it can handle both positional and named parameters if you decide to go down the route. Now go out and start writing your own shortcodes . I promise it’ll make your life easier. Feel free to download this shortcode snippet on GitHub .

comments powered by Disqus