Hugo brings pre-defined shortcodes to expand your content beyond the basic markdown syntax. The official docs explain them quite well. The goal of this post is to distill the essential information into concise, ready-to-use Minimal Working Examples (MWE) for easy copy-pasting. As well as really understanding the code behind so you can create your own shortcodes.
Index - Click to expand π
Go and Hugo code basics
Basic coding skills are indispensable to understand later shortcode templates.
This guide constanstly links official documention for reference. Though a basic knowledge of Go’s syntax is expected, this is extremily summarized in the ongoing section.
Semi-extensive HTML proficiency is a must too.
Shortcode templates vs call
Before we go any deeper, let’s make sure we’re on the same page. In this post:
- A shortcode call refers to Markdown snippets that use a shortcode, e.g., 
{{< qr text="https://gohugo.io" >}}. - A shortcode declaration is the HTML (or template) code that defines these Markdown snippets, such as the qr.html script. In the Hugo documentation this is referred to as a shortcode template or sometimes simply a shortcode or template.
 
Go basics
Actions -data evaluations or control structures- are delimited by {{ and }}.
{{- and -}} trim whitespace to the left and right respectively.
MWE:
{{- "  foo  " }}  // "foo  "
{{ "  foo  " -}}  // "  foo"
{{- "  foo  " -}} // "foo"
{{/* and */}} wrap a comment. Read docs and this brief article.
{{ . }} and with. Within a template, the dot (.) represents the current context.
We rebind the context to another value or object within range or with blocks.
Read Current context.
Becoming familiar with the dot is essential for understanding shortcode templates.
Further reading:
- Go’s Table of Contents
 - Specially the text/template documentation.
 
if-else-end
The with statement is an important feature to master.
We have already explained its usage with the dot context.
Don’t worry if for now it sounds all very abstract, when we focus on each shortcode template we’ll see how it’s used.
The if statement is equally essential.
It is widely used for validations and other conditional logic.
Recommended reviewing the documentation linked above before proceeding.
Other actions (like define or errorf) may appear in certain templates.
However, their behavior can usually be inferred from the surrounding code.
.Get
Positional arguments
- Create a shortcode template [script] via
 
printfandGet:echo '{{ printf "%s %s." (.Get 0) (.Get 1) }}' > layouts/shortcodes/myshortcode.html- Just 
Get:echo '{{ .Get 0 }} {{ .Get 1 }}.' > layouts/shortcodes/myshortcode.html - Use the Params method to access the arguments as a collection.
Read Argument collection.
echo '{{ .Params 0 }} {{ .Params 1 }}.' > layouts/shortcodes/myshortcode.html 
- Insert the respective shortcode call (usage) in a markdown file
echo '---\ntitle: "Test Shortcode"\n---\n\n{{< myshortcode "Hello" "world" >}}' > content/en/myshortcode.md - Build 
hugo server --disableFastRender - Check in the browser (http://localhost:1313/myshortcode/) that it’s rendered to 
Hello world.(actually to<section class="post-content">Hello world.</section>since Hugo renders plain line in sections elements). 
This time create a <p> HTML element with bold text:
echo '<p style="font-weight: bold">{{ .Get 0 }} {{ .Get 1 }}.</p>' > layouts/shortcodes/myshortcode.html- or 
echo '{{ printf "<p style=\"font-weight: bold\">%s %s.</p>" (.Get 0) (.Get 1) | safeHTML }}' > layouts/shortcodes/myshortcode.html 
Which renders to
<section class="post-content">
  <p style="font-weight: bold">Hello world.</p>
</section>
Named arguments
- Choose one among
 
echo '{{ printf "%s %s." (.Get "greeting") (.Get "firstName") }}' > layouts/shortcodes/myshortcode.htmlecho '{{ .Get "greeting" }} {{ .Get "firstName" }}.' > layouts/shortcodes/myshortcode.htmlecho '{{ .Params.greeting }} {{ .Params.firstName }}.' > layouts/shortcodes/myshortcode.html
echo '---\ntitle: "Test Shortcode"\n---\n\n{{< myshortcode greeting="Hello" firstName="world" >}}' > content/en/myshortcode.mdhugo server --disableFastRender- http://localhost:1313/myshortcode/
 
Both named and positional args
Accept both named and positional arguments, but not at the same time. Read IsNamedParams.
Vimeo shortcode is a good example of this.
There the id in the shortcode call can be either the first (and only) positional parameter or a named parameter (as rest of the arguments, though these others are optional).
TL;DR. All next are equivalent shortcode calls (with optional parameters assigned to default values [of future respective arguments]):
{{< vimeo    55073825 >}}
{{< vimeo id=55073825 >}}
{{< vimeo    55073825 loading=eager >}}
{{< vimeo id=55073825 loading=eager >}}
{{< vimeo    55073825 loading=eager allowFullScreen=true >}}
{{< vimeo id=55073825 loading=eager allowFullScreen=true >}}
Note. Whitespaces added for readability. They will be trimmed anyway.
but not at the same time
Therefore, next calls are invalid:
{{< vimeo    55073825 id=55073825 >}} // id was already assigned by the first positional arg
{{< vimeo id=55073825    55073825 >}} // id was already assigned by the first named arg
{{< vimeo    55073825 id=55073825 loading=eager >}} // as 1st call
{{< vimeo id=55073825    55073825 loading=eager >}} // as 2nd call
{{< vimeo    55073825    eager >}} // Max one positional arg (the id if any) in this template
Details
Create a HTML <details>
A disclosure widget in which information is visible only when the widget is toggled into an open state.
MWE
{{< details summary="See the details" >}}
This is a **bold** word.
{{< /details >}}
Hugo renders previous shortcode call to next HTML chunk:
<details>
  <summary>See the details</summary>
  <p>This is a <strong>bold</strong> word.</p>
</details>
Which looks like this in your browser (click to toggle un-/fold):
See the details
This is a bold word.
Next is pre-expanded (open attribute with boolean true is passed):
{{< details summary="See the details" open=true >}}
This is a **bold** word.
{{< /details >}}
See the details
This is a bold word.
Practical usage
Fold your headers index (table of contents):
{{< details summary="**Index** - Click to expand π" >}}
  * [header-1](#header-1)
    * [header-1-1](#header-1-1)
      * [header-1-1-1](#header-1-1-1)
      * [header-1-1-2](#header-1-1-2)
  * [header-2](#header-2)
  ...
{{< /details >}}
This is applied in current post, jump to index.
Tip. With Vim’s plugin vim-markdown
one can directly run :InsertToc to get the markdown’s table of contents.
Or :InsertNToc for the Numbered version.
This plugin and many others are showcasted in my vim-plugins-screenshots repo.
Declaration
In Hugo’s repo, script details.html contains:
{{- /*
Renders an HTML details element.
@param {string} [class] The value of the element's class attribute.
@param {string} [name] The value of the element's name attribute.
@param {string} [summary] The content of the child summary element.
@param {string} [title] The value of the element's title attribute.
@param {bool} [open=false] Whether to initially display the content of the details element.
@reference...
@examples...
*/}}
{{- /* Get arguments. */}}
{{- $class := or (.Get "class") "" }}
{{- $name := or (.Get "name") "" }}
{{- $summary := or (.Get "summary") "Details" }}
{{- $title := or (.Get "title") "" }}
{{- $open := false }}
{{- if in (slice "false" false 0) (.Get "open") }}
  {{- $open = false }}
{{- else if in (slice "true" true 1) (.Get "open") }}
  {{- $open = true }}
{{- end }}
{{- /* Render. */}}
<details
  {{- with $class }} class="{{ . }}" {{- end }}
  {{- with $name }} name="{{ . }}" {{- end }}
  {{- with $open }} open {{- end }}
  {{- with $title }} title="{{ . }}" {{- end -}}
>
  <summary>{{ $summary | .Page.RenderString }}</summary>
  {{ .Inner | .Page.RenderString (dict "display" "block") -}}
</details>
This template generates a <details>
HTML element with dynamic attributes and content.
Parameters declaration
These params are usually called arguments in functions of other prog-languages, or attributes in HTML.
But arguments is later used to refer those objects accesed via $VAR, like $class,
which value of course will depend (here match) the param class value or be empty string if this param is not present in the shortcode call ({{- $class := or (.Get "class") "" }});
but we will cover this in the Get values section.
Btw. this is the reason why in the param-s declaration, that we cover now, there is no default value to assign.
For example false is not the default value for the param open, this param just values what it’s passed in the shortcode call (here by positional arguments) or nil if not passed.
Positional vs named parameters is coverd in the .Get section.
In this shortcode we have comments for each @param (class, name, summary, title and open) with the following info:
- A name: 
class,name,summary,titleoropen - A type: 
stringorbool - A description, e.g. 
Whether to initially display the content of the details element - Optional/required status. All these parameters are optional,
because their names are wrapped in square brackets (
[]). For example, the paramclassis described as (making the[]bold) as@param {string}[class]The value of...instead of@param {string} class The value of...Later, in the QR-declaration, we will explore another shortcode template which needs an obligatory parameter. 
Let this sink in: these parameter’s descriptions are in comments (inbetween {{- /* and */}} comment delimiters),
which could be removed,
while the real logic of the template, that will make a parameter optional or obligatory, appears in code lines that actually run
(the Get arguments. and Render. blocks for the ongoing template).
Get values
Get arguments. chunk extracts the non empty parameters values and assigns them to the respective argument.
If the parameter was not in the shortcode call (or was an empty string), the argument is set a default value ("" for every arg except $summary and $open).
Lets understand how it works:
- The variable 
$classis assigned to either (:= or):- The classes passed in the shortcode call (e.g. 
{{< details class="m-0 w-75" >}}) thanks to(.Get "class"), - or an empty string (
"") if no class was passed in the shortcode call (or passed an empty string like{{< details class="" >}}). 
 - The classes passed in the shortcode call (e.g. 
 - Analogous for 
$nameand$title. $summarysetting is alike previous ones, but if nosummaryparam is passed in the shortcode call, then its arguments is set to"Details".- The variable 
$openis pre-set tofalseand then assigned to either:falseif(.Get "open")is in the list of values("false", false, 0). I.e. next three shortcode calls are equivalent:{{< details open="false" >}}{{< details open=false >}}{{< details open=0 >}}
- or to 
trueif(.Get "open")is one of next values:"true",trueor1. 
 
Finally we need to know how it’s handled the content of the children of the <details> (siblings of the <summary> element).
I.e. the actual content folded of the <details> element.
It’s used .Inner, docs:
Returns the content between opening and closing shortcode tags, applicable when the shortcode call includes a closing tag.
Thus, in the details shortcode call, we have:
- An opening shortcode tag, .e.g 
{{< details summary="See the details" >}} - The closing shortcode tag 
{{< /details >}} - And the content in between, .e.g. 
This is a **bold** word. 
The .Inner appears in the penultimate line of details.html:
73  <summary>{{ $summary | .Page.RenderString }}</summary>
74  {{ .Inner | .Page.RenderString (dict "display" "block") -}}
75</details>Where the | .Page.RenderString (dict "display" "block") ensures block-level rendering (for paragraphs, lists, etc.).
The .Page.RenderString method provides next excellent example:
{{ $s := "An *emphasized* word" }}
{{ $opts := dict "display" "block" }}
{{ $s | .RenderString $opts }} β <p>An <em>emphasized</em> word</p>
The documentation also explains the block display effect. Without it we will no longer get a <p>aragraph:
{{ $s := "An *emphasized* word" }}
{{ $s | .RenderString }} β An <em>emphasized</em> word
{{/* Alternative with explicit display. */ }}
{{ $opts := dict "display" "inline" }}
{{ $s | .RenderString $opts }} β An <em>emphasized</em> word
Render
Let’s overview the template:
- Intially there are some comments describing the 
@parameters (see the Parameters declaration), the docs@referenceand@example-s of shortcode calls. Get arguments.chunk uses the non empty parameters to populate the respective [positional] arguments. If the parameter was not in the shortcode call (or was an empty string), the argument is set a default value. Details in previous Get values pararagraph.- Finally the 
Render.code lines construct the HTML element<details>thanks to the arguments. We focus on this. 
Basic HTML knowledge:
- Attributes like 
openare considered true simply by being present. They are called boolean attributes in HTML. - Attributes like 
classrequire an explicit value (e.g.,class="c1 c2"). These are known as normal attributes or valued attributes. 
Let’s start with the boolean attribute open.
An HTML <details> might:
- Contain it like 
<details open><summary>Expand</summary><p>...</p></details> - Or not 
<details><summary>Expand</summary><p>...</p></details> 
Also, we expect
- When 
$open=truethis renders to<details open> - When 
$open=falsethis renders to<details> 
Above is rendered via {{- with $open }} open {{- end }}.
Where,
- The syntax 
{{- with $VAR }} CONTENT {{- end }}just printsCONTENT(here the stringopen) if$VAR($openhere) istrue. Remember that theif-else-endinGet argumentsreplaces falsy values (string"false"and number0) with booleanfalseand viceversa ("true"and1totrue). - Check Hugo’s 
with-else-end. 
To not confuse the string to add (open)
with the boolean argument that enables this ($open)
we could define a new variable $openAttribute:
{{- $openAttribute := "open" }}
...
{{- with $open }} $openAttribute {{- end }}
Do not apply this alternative! It’s just for learning purposes.
The original code, with the literal string open, is clearer in this simple case.
Let’s now study how it’s rendered the normal attribute class.
An HTML <details> can:
- Contain a list of 
class-es like<details class="c1 c2"><summary>Expand</summary><p>...</p></details> - Or it can skip the 
classattribute like<details><summary>Expand</summary><p>...</p></details> 
Previously we did .Get the $class: the given string value (positional parameter class passed), except if what was passed was falsy
(empty string or shortcode call without this param at all),
then $class would fall back to an empty string ("").
Now we will render it via {{- with $class }} class="{{ . }}" {{- end }}
- The syntax is 
{{- with $VAR }} ATTRIBUTE="{{ . }}" {{- end }}. Which means next. If the variable$VAR($classhere) is true-ish (i.e. not empty since its astringvar) then it adds the stringATTRIBUTE=""(hereclass=""), and inbetween the double quotes puts the value of the variable$VAR(string$classhere) via{{ . }}. Otherwise, if$VARis falsy, no content is added at all, not evenclass="". {{-and-}}trim whitespaces
To not confuse the string to add inside the double quotes (string variable $class)
with the string that indicates the HTML attribute (class followed by =)
we could define a new variable $classAttribute:
{{- $classAttribute := "class" }}
...
{{- with $class }} $classAttribute="{{ . }}" {{- end }}
Docs
Visit https://gohugo.io/shortcodes/details/
- To learn how to override Hugo’s embedded 
detailsshortcode. Or just read the Override a default shortcode section in the ongoing post. - Find out more about the parameters
(though here 
@-paramsand$-arguments are somehow mixed an just calledArguments). E.g. it explains thatopen (bool)serves to initially display (unscroll) the content and that its default value isfalse. - CSS styling
 
Figure
Create a HTML <figure>
Represents self-contained content, potentially with an optional caption.
MWE
{{< figure
  src="https://images.pexels.com/photos/7210754/pexels-photo-7210754.jpeg"
  alt="Unrecognizable woman walking dogs on leashes in countryside"
  link="https://www.pexels.com/photo/unrecognizable-woman-walking-dogs-on-leashes-in-countryside-7210754/"
  caption="Unrecognizable *woman* walking **dogs** on leashes in ***countryside***. Class `bg-black`. Check [this **link**\!](/blogs/instagram_mosaic/)"
  class="m-0 w-75 bg-black p-4"
  loading="lazy"
>}}
Hugo renders previous shortcode call to next HTML:
<figure class="m-0 w-75 bg-black p-4">
  <a href="https://www.pexels.com/...">
    <img 
      src="https://images.pexels.com/..." 
      alt="Unrecognizable woman walking..."
      loading="lazy"
    >
  </a>
  <figcaption>
    <p>Unrecognizable <em>woman</em> walking <strong>dogs</strong> on leashes in <strong><em>countryside</em></strong>. Class <code>bg-black</code>. Check <a href="/blogs/instagram_mosaic/">this <strong>link</strong>!</a></p>
  </figcaption>
</figure>
Which looks like this:

Unrecognizable woman walking dogs on leashes in countryside. Class bg-black. Check this link!
The caption applied many Markdown elements to highlight that one can use them there.
Note. class="m-0 w-75 bg-black p-4" requires Tachyons.
Enable it adding next to layouts/partials/custom_head.html
<!-- Tachyons for CSS -->
<!-- https://tachyons.io/ -->
<link rel="stylesheet" href="https://unpkg.com/tachyons@4.12.0/css/tachyons.min.css"/>
Btw. This photo is used in my post How to create an Instagram mosaic on the terminal. Which may interest you if you are active on Instagram.
Off-topic. loading attribute is supported in HTML5.
To instruct the browser to defer loading of images/iframes that are off-screen until the user scrolls near them
Check it out:
- Refresh current website removing the cache - try a private tab
 - Run next JS code in the browser’s console.
It will print 
https://images.pexels.com/photos/7210754/pexels-photo-7210754.jpeg Not loaded yet 
document.querySelectorAll('img[loading="lazy"]').forEach(img => {
  console.log(img.src, img.complete ? 'Loaded' : 'Not loaded yet');
});
- Then scroll till that 
<figure>(previous dogs picture). - And run it again. Now it echoes 
https://images.pexels... Loaded 
Docs
Visit https://gohugo.io/shortcodes/figure/ to learn more about this shortcode.
- Arguments - 
src,alt,link,caption, etc. - Image location
 
Apply image processing to resize, crop, rotate, filter, and convert images in your custom shortcode template. This reference is is extremely well documented, I will just underline that:
- Hugo uses smart cropping by default but also allows custom anchor points for crops.
 - Processed images are cached in a resources directory to optimize build times.
Thus, if you changed image processing methods or removed images run the garbage collection 
hugo --gccommand. Reference. 
Declaration
In Hugo’s repo read figure.html.
Most of next shortcode template is easy to understand if you read
the comprensive explanation of the display shortcode in previous section.
Just few chunks are a little tricky, like
{{- if .Get "link" -}}
  <a href="{{ .Get "link" }}"{{ with .Get "target" }} target="{{ . }}"{{ end }}{{ with .Get "rel" }} rel="{{ . }}"{{ end }}>
{{- end -}}
Read the if-else-end section. Then indent these three lines for clarity:
{{- if .Get "link" -}}
  <a href="{{ .Get "link" }}"
    {{- with .Get "target" }} target="{{ . }}"{{ end }}
    {{- with .Get "rel" }} rel="{{ . }}"{{ end }}
  >
{{- end -}}
This code chunk is reading the positional parameters link, target and rel passed via a shortcode call like next
{{< figure link="https://www.nps.gov/zion/index.htm" target="_blank" rel="noopener noreferrer" >}}
And directly rendering the <a> children of the <figure> (see next code block),
without populating intermediate argumentes $link, $target and $rel.
On the other hand, for more complex HTML attributes this intermediate step is needed [or deeply recommended], e.g. in the details template.
<a href="https://www.nps.gov/zion/index.htm"
   target="_blank"
   rel="noopener noreferrer">
Notice that a [non empty] positional parameter link is mandatory to construct the <a>, while target and rel are optional.
I.e. if the condition {{- if .Get "link" -}} isn’t true then no code till {{- end -}} is executed.
We end this shortcode with a little challenge.
Can you rewrite the previous three lines (six if indented) using with instead of if?
Solution - Click to expand
{{- with .Get "link" -}}
  <a href="{{ . }}"
    {{- with $.Get "target" }} target="{{ . }}"{{ end }}
    {{- with $.Get "rel" }} rel="{{ . }}"{{ end }}
  >
{{- end -}}
And can we actually apply this replacement in the figure.html template?
Solution - Click to expand
Yes, the replacement is valid.
There is no dot conflict.
We verified that no surrounding with was employing its dot (.) within the if block being replaced.
Actually there is no parent with at all. The one in the first line is an inline with (it starts and ends in the same line),
thus it cannot interfere with our context.
<figure{{ with .Get "class" }} class="{{ . }}"{{ end }}>
Gist shortcode
Deprecated in v0.143.0. Check the docs for workarounds.
Highlight
Visit https://gohugo.io/shortcodes/highlight/ for reference.
Insert syntax-highlighted code.
MWE
{{< highlight go "linenos=inline, hl_lines=3 6-8, style=monokai" >}}
package main
import "fmt"
func main() {
    for i := 0; i < 3; i++ {
        fmt.Println("Value of i:", i)
    }
}
{{< /highlight >}}
Hugo renders this to:
1package main
2
3import "fmt"
4
5func main() {
6    for i := 0; i < 3; i++ {
7        fmt.Println("Value of i:", i)
8    }
9}Syntax highlighting
Few more to point out. The docs are self-explanatory. And the MWE above is easy to follow.
You might just need the link for the Syntax highlighting styles.
The default can be set in config.toml.
```), depend on that config too.
Observe next code chunk for Hugo:```python
  import numpy as np
  np.random.seed(42)
```Hugo renders this to:
import numpy as np
np.random.seed(42)
Since my config.toml has
[markup]
  [markup.highlight]
    style = 'solarized-dark'
Check it:
fetch('https://raw.githubusercontent.com/juanMarinero/juanmarinero.github.io/refs/heads/main/hugo.toml')
  .then(r => r.text())
  .then(t => console.log(t.split('\n').filter(l => l.includes('style'))));
Finally some practical examples. View the highlighted code lines in the post The Math Is Haunted by Overreacted. These help guide the reader’s attention to specific parts of code chunks.
Declaration
First understand the comprensive explanation of the display shortcode in previous section.
Specially the if-else-end section.
Hugo’s repo hightlight.html content is, once indented:
{{ if len .Params | eq 2 }}
  {{ highlight (trim .InnerDeindent "\n\r") (.Get 0) (.Get 1) }}
{{ else }}
  {{ highlight (trim .InnerDeindent "\n\r") (.Get 0) "" }}
{{ end }}
For .Params take a quick look at:
- The param method
 - The basics shortcode templates created in Positional arguments and Named arguments sections
that apply the syntax 
{{ Params POSITION }}or{{ Params.NAME }} 
SHORTCODE.Inner
and its indented version
SHORTCODE.InnerDeindent
return the content between opening and closing shortcode tags.
E.g. the content in between {{< highlight go "linenos=inline, hl_lines=3 6-8, style=monokai" >}} and {{< /highlight >}}, i.e. the Go code.
Previous shortcode call had two arguments parameters:
go- and 
"linenos=inline, hl_lines=3 6-8, style=monokai" 
In summary,
trim .InnerDeindent "\n\r"cleans the code content(.Get 0)gets the language (e.g.go)(.Get 1)prints the options string (e.g."linenos=inline, hl_lines=3")
Therefore, in previous shortcode call the {{ if len .Params | eq 2 }} will trigger true, and the highlight function is called with cleaned code, language and options.
Wrapping up:
Template Function
hightlight.htmlmakes all previous described. I.e., this shortcode template pass code lines, code language and some attributes (or it can pass none of any/all) from the shortcode call to thehighlightfunction, which is bolded next:{{highlight(trim .InnerDeindent "\n\r") (.Get 0) (.Get 1) }}This
highlightof the shortcode template resolves tofunc (ns *Namespace) Highlight(s any, lang string, opts ...any) (template.HTML, error)intransform.goWhich then calls the Go Function highlight.go.
And the previous uses Chroma to generate HTML5-compliant syntax highlighting
Visit https://gohugo.io/shortcodes/instagram/ for reference.
MWE
A video from Instagram
{{< instagram CxOWiQNP2MO >}}
Renders to next. Press to play.
Post of photos from Instagram are supported too:
{{< instagram CxOWiQNP2MO >}}
Declaration
First understand the comprensive explanation of the display shortcode in previous section.
Specially the if-else-end.
Hugo’s repo instagram.html content is:
{{- $pc := site.Config.Privacy.Instagram -}}
{{- if not $pc.Disable -}}
  {{- with .Get 0 -}}
    {{- template "render-instagram" (dict "id" . "pc" $pc) -}}
  {{- else -}}
    {{- errorf "The %q shortcode requires a single positional parameter, the ID of the Instagram post. See %s" .Name .Position -}}
  {{- end -}}
{{- end -}}
{{- define "render-instagram" -}}
  <blockquote
    ...
    data-instgrm-permalink="https://www.instagram.com/p/{{ .id }}"
    ...
  >
  ...
  </blockquote>
  {{- if not .pc.Simple -}}
    <script async src="https://www.instagram.com/embed.js"></script>
  {{- end -}}
{{- end -}}
Read the Privacy subsection of this shortcode.
config.toml must use default values for Privacy.Instagram or explicitly set it to false.
This settings are read via site.Config.Privacy into $pc.
This way {{- if not $pc.Disable -}} is true.
Watch if hugo config echoes something alike:
> hugo config | grep -C 5 "instagram"
[privacy]
  [privacy.instagram]
--
[services]
  [services.instagram]
Furthermore at least one param is required in the shortcode call, the ID of the Instagram post: {{- with .Get 0 -}}.
See Hugo’s with-else-end.
If both requirements (Privacy.Instagram and ID) are satisfied then the shortcode template runs {{- template "render-instagram" (dict "id" . "pc" $pc) -}},
where that function is after the if-else-end defined: {{- define "render-instagram" -}}...{{- end -}}.
The template function executes a defined template called render-instagram.
Docs about go-template.
MWE:
{{ template "foo" (dict "answer" 42) }}
{{ define "foo" }}
  {{ printf "The answer is %v." .answer }}
{{ end }}
The arguments passed to that defined template are mapped to (dict "id" . "pc" $pc):
- The key 
"id"maps to the first positional parameter since the dot is binded to the enclosing{{- with .Get 0 -}}. - While the key 
"pc"maps to the variable$pcdefined assite.Config.Privacy.Instagramas we saw in the first line. 
The proper HTML-render with that two arguments is out of this scope. But I summary what concerns to our arguments:
- The ID is accesed via 
{{ .id }}and it’s to be found:- Inside the 
<blockquote>attributes:data-instgrm-permalink="https://www.instagram.com/p/{{ .id }}" - And in 
('blockquote div a').href 
 - Inside the 
 - Finally the 
render-instagramtemplate checks if the Instagram policy isfalsein configs via ({{- if not .pc.Simple -}}), if so then Hugo creates a static card without JavaScript for images only (no videos). Details in Privacy. 
Param
Visit https://gohugo.io/shortcodes/param/ for reference.
MWE
At top of current markown there’s a title param.
It can be read with:
Current post title is **{{% param "title" %}}**.
Which renders to:
Current post title is Hugo shortcodes: step by step.
In config.toml there’s a favicon param:
Global favicon value is `{{% param "favicon" %}}`.
Outputs:
Global favicon value is images/favicon.png.
It could also be accesed creating a shortcode template with {{ .Site.Params.favicon }}, read https://gohugo.io/methods/site/params/.
Not all params are accessible via {{ .Site.Params.<param-name> }}. Same applies for other SITE config settings. Full list can be found at https://gohugo.io/methods/site/.
Let’s exemplify how to access the global title (.Site.Title), defined in config.toml, we must instead create our own shortcode template:
- Create a HTML shortcode with
echo 'Global title: <strong>{{ .Site.Title }}</strong>.' > layouts/shortcodes/myshortcode.html - Insert it in a markdown 
echo '---\ntitle: "Test Shortcode"\n---\n\n{{< myshortcode >}}' > content/en/myshortcode.md - Build 
hugo server --disableFastRender - Check in the browser (http://localhost:1313/myshortcode/) that it’s rendered to: Global title: J. Marinero…
 
hugo config also has multiple contacts:
[params]
  copyright = '...'
  description = 'Juan Marinero...'
  favicon = 'images/favicon.png'
  [[params.contacts]]
    icon = 'fa fa-phone'
    label = 'phone'
    url = 'tel:...'
    value = '...'
Replace previous shortcode $EDITOR layouts/shortcodes/myshortcode.html with
{{ with (index site.Params.contacts 2) }}
By 3rd index:<br>
- Label: {{ .label }}<br>
- Value: {{ .value }}
{{ end }}
{{ with (index (where site.Params.contacts "label" "GitHub") 0) }}
  <br><br>
If <code>label</code> values <code>GitHub</code>:<br>
- URL: {{ .url }}"<br>
- Value: {{ .value }}
{{ end }}
To get:
- Label: GitHub
- Value: github.com/juanMarinero
If
label values GitHub:- URL: https://github.com/juanMarinero"
- Value: github.com/juanMarinero
Declaration
First understand the comprensive explanation of the display shortcode in previous section.
Specially the if-else-end section.
Hugo’s repo param.html content is, once indented:
{{- $name := (.Get 0) -}}
{{- with $name -}}
  {{- with ($.Page.Param .) }}
    {{ . }}
  {{ else }}
    {{ errorf "Param %q not found: %s" $name $.Position }}
  {{ end -}}
{{- else }}
  {{ errorf "Missing param key: %s" $.Position }}
{{ end -}}
.Page methods docs here. .Page.Param specific in this link.
Let’s suppose that we have {{% param "favicon" %}} as shortcode call.
Then $name values "favicon" (the first positional parameter of the shortcode call).
{{- with $name -}} is true-ish since $name exists (the shortcode call passed a parameter) and is not an empty string (this parameter passed wasn’t "").
{{- with ($.Page.Param .) }} would be {{- with ($.Page.Param $name) }} because the dot is binded by the enclosing {{- with $name -}}.
The parentheses serve as a grouping operator for Go.
In vanille Go we would just write page.Param("favicon").
But in this shortcode template we need the grouping ($.Page.Param "favicon") to force evaluation of the entire method call first.
Without parentheses ({{- with $.Page.Param . -}}) the result would be [again!] just the previous dot (of upper with),
the string "favicon", as long as $.Page.Param is true-ish, instead of the desired value of page.Param("favicon").
The Go’s precedence rules are just this way, the parentheses force the evaluation of its content first.
I.e. the equivalent Go code without proper grouping could be as:
methodValue := $.Page.Param  // Gets the method as a value
if methodValue != nil {
  dot = .  // Sets dot to the PREVIOUS context (e.g., "favicon")
}
The need and purpose of the parentheses is demonstrated.
In summary, this second with achieves:
- Checks that 
page.Param("favicon")istrue-ish. - Assigns that value to dot
 
This way, and finally, the forth line is just a dot {{ . }}, inside that second with it’s the parentheses value (e.g. page.Param("title")) as just explained.
1{{- $name := (.Get 0) -}}
2{{- with $name -}}
3  {{- with ($.Page.Param .) }}
4    {{ . }}
5  {{ else }}
6  ...QR
Visit https://gohugo.io/shortcodes/qr/ for reference.
MWE
{{< qr text="https://gohugo.io" >}}
Renders to

Now check if the QR does actually points to https://gohugo.io.
- Copy the image URL above, or just 
https://gohugo.io/images/qr/qr_a1aee921a2e8180d.png - Go to an online QR code reader via 
Paste URLof image. Like https://www.minifier.org/qr-code-scanner - Test it
 
We are not limited to URLs. The docs explain how to use this shortcode to generate QRs of:
- Phone numbers
 - Contact information in the vCard format
 
Declaration
First understand the comprensive explanation of the display shortcode in previous section.
Specially the if-else-end section.
Hugo’s repo qr.html content is basically:
{{- /* Constants. */}}
{{- $validLevels := slice "low" "medium" "quartile" "high" }}
{{- $minimumScale := 2 }}
{{- /* Get arguments. */}}
{{- $text := or (.Get "text") (strings.TrimSpace .Inner) "" }}
{{- $level := or (.Get "level") "medium" }}
{{- $scale := or (.Get "scale") 4 }}
{{- $targetDir := or (.Get "targetDir") "" }}
{{- $alt := or (.Get "alt") "" }}
{{- $class := or (.Get "class") "" }}
{{- $id := or (.Get "id") "" }}
{{- $title := or (.Get "title") "" }}
{{- $loading := or (.Get "loading") "" }}
{{- /* Validate arguments. */}}
{{- $errors := false}}
{{- if not $text }}
  {{- errorf "The %q shortcode requires a %q argument. See %s" .Name "text" .Position }}
  {{- $errors = true }}
{{- end }}
{{- if not (in $validLevels $level) }}
  {{- errorf "The %q argument passed to the %q shortcode must be one of %s. See %s" "level" .Name (delimit $validLevels ", " ", or ") .Position }}
  {{- $errors = true }}
{{- end }}
{{- if or (lt $scale $minimumScale) (ne $scale (int $scale)) }}
  {{- errorf "The %q argument passed to the %q shortcode must be an integer greater than or equal to %d. See %s" "scale" .Name $minimumScale .Position }}
  {{- $errors = true }}
{{- end }}
{{- /* Render image. */}}
{{- if not $errors }}
  {{- $opts := dict "level" $level "scale" $scale "targetDir" $targetDir }}
  {{- with images.QR $text $opts -}}
    <img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}"
      {{- with $alt }} alt="{{ $alt }}" {{- end }}
      {{- with $class }} class="{{ $class }}" {{- end }}
      {{- with $id }} id="{{ $id }}" {{- end }}
      {{- with $title }} title="{{ $title }}" {{- end -}}
      {{- with $loading }} loading="{{ $loading }}" {{- end -}}
    >
  {{- end }}
{{- end -}}
Code explanation.
- Assigning the arguments values (
$text,$level,$scale, etc.) dependent on the parameters of the shortcode call or default values. - The arguments values are validated.
If the parameter 
textwas not passed in the shortcode call or if other parameters trigger an invalid situation (like incompatiblelevelandvalidLevels) then$errorsis set totrue. Which will cause the image to not be rendered (the{{- if not $errors }}condition). The requirement for the presence of the parametertextis explained in the comment@param {string} text The text to encode...where itstextis not wrapped in square brackets. - The QR object itself is achieved calling the 
images.QRfunction with the arguments$text(e.g. an URL) and$opts(a dictionay with some of the rest arguments). We could add (and bold) some parentheses to clarify the evaluation order:{{- with(images.QR $text $opts)-}}. - That function returns an object. Let’s suppose that that object is 
true-ish (ideally an image with relPermalink, width and height for later use). Then the dot.is binded to this QR object, thanks to the{{ with VAR }}syntax, for the currentwithscope. - Inside the 
withblock it’s rendered the HTML element<img>providing: 
- Properties of the dot (the QR-object returned by the 
images.QRfunction), respectively{{ .RelPermalink }}(for thesrcattribute),{{ .Width }}and{{ .Height }}: class,id, etc.<img>-attributes value the respective arguments ($class,$id, etc.). Checking each string (yes, qr.html initial lines define all the shortcode call parameters as string, exceptscale, and logically the respective template arguments are also strings) and if they are truthy then their respectivewithwill assign its value to the homonym attribute. For example{{- with $alt }} alt="{{ $alt }}" {{- end }}
67{{- /* Render image. */}}
68{{- if not $errors }}
69  {{- $opts := dict "level" $level "scale" $scale "targetDir" $targetDir }}
70  {{- with images.QR $text $opts -}}
71    <img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}"
72      {{- with $alt }} alt="{{ $alt }}" {{- end }}
73      {{- with $class }} class="{{ $class }}" {{- end }}
74      {{- with $id }} id="{{ $id }}" {{- end }}
75      {{- with $title }} title="{{ $title }}" {{- end -}}
76      {{- with $loading }} loading="{{ $loading }}" {{- end -}}
77    >
78  {{- end }}
79{{- end -}}Ref
Docs:
This shortcode is obsolete […] use the embedded link render hook or create your own
Create a link render hook to override the rendering of Markdown links to HTML.
MWE
[Post 1](/posts/post-1 "My first post")
 ------  -------------  -------------
  text    destination       title
Template script in render-link.html:
{{- $u := urls.Parse .Destination -}}
{{- $href := $u.String -}}
{{- if strings.HasPrefix $u.String "#" -}}
  {{- $href = printf "%s#%s" .PageInner.RelPermalink $u.Fragment -}}
{{- else if and $href (not $u.IsAbs) -}}
  {{- $path := strings.TrimPrefix "./" $u.Path -}}
  {{- with or
    ($.PageInner.GetPage $path)
    ($.PageInner.Resources.Get $path)
    (resources.Get $path)
  -}}
    {{- $href = .RelPermalink -}}
    {{- with $u.RawQuery -}}
      {{- $href = printf "%s?%s" $href . -}}
    {{- end -}}
    {{- with $u.Fragment -}}
      {{- $href = printf "%s#%s" $href . -}}
    {{- end -}}
  {{- end -}}
{{- end -}}
<a href="{{ $href }}" {{- with .Title }} title="{{ . }}" {{- end }}>{{ .Text }}</a>
{{- /**/ -}}
Explanation.
- When you write a Markdown link like 
[Post 1](/posts/post-1 "My first post")Hugo parses it into a [dot] structured object with these fields: 
.Text:"Post 1", the link text. To be found in:</a href="...">{{ .Text }}</a>..Title:"My first post"(the optional title attribute). Also in the second to last line:{{- with .Title }} title="{{ . }}" {{- end }}..Destination:"/posts/post-1"is the URL/target to be parsed.
{{- $u := urls.Parse .Destination -}}calls theurls.Parsefunction on the.Destinationparamater. The return populates the$uURL structure:
$u.String: full URL (/posts/post-1)$u.Path: path part ("/posts/post-1").$u.Fragment: empty (unless URL had#section)$u.RawQuery: empty (unless URL had?foo=bar)$u.IsAbs:false. This method reports whether the URL is absolute (with a non-empty scheme likehttps://...). Examples in docs
The urls.Parse reference provides a more complete example:
{{ $url := "https://example.org:123/foo?a=6&b=7#bar" }}
{{ $u := urls.Parse $url }}
{{ $u.String }}        // https://example.org:123/foo?a=6&b=7#bar
{{ $u.IsAbs }}         // true
{{ $u.Scheme }}        // https
{{ $u.Host }}          // example.org:123
{{ $u.Hostname }}      // example.org
{{ $u.RequestURI }}    // /foo?a=6&b=7
{{ $u.Path }}          // /foo
{{ $u.RawQuery }}      // a=6&b=7
{{ $u.Query }}         // map[a:[6] b:[7]]
{{ $u.Query.a }}       // [6]
{{ $u.Query.Get "a" }} // 6
{{ $u.Query.Has "b" }} // true
{{ $u.Fragment }}      // bar
- The remaining code lines concatenate strings into the variable 
$href(for thehrefattribute) to construct the<a>HTML element: 
<a href="/resolved/path/to/post-1" title="My first post">Post 1</a>
OK, understood. But…why not just use the default markdown [text](url)?
Well, among other features, this render hook adds smart resolution for:
- Relative links: ensures 
/posts/post-1points to the correct permalink (e.g.,/blog/post-1/). - Fragments: fixes 
#sectionlinks to work with Hugo’s URL structure. 
Relref
Docs:
This shortcode is obsolete […] use the embedded link render hook or create your own
This is the same warning message as for Ref. Read the Ref section to grasp how an embedded link render hook works.
Vimeo
MWE
{{< vimeo 55073825 >}}
Renders the Vimeo video https://vimeo.com/channels/staffpicks/55073825 to:
The docs:
- Explain each argument
 - How to overwrite this shortcode call to run your own shortcode template
 - And the Hugo configs related
 
Declaration
First understand the comprensive explanation of the display shortcode in previous section.
Vimeo’s shortcode template is vimeo.html.
But if in your Hugo configs has set true to simple’s Vimeo privacy (like next hugo.toml extract)
then it runs vimeo_simple.html
({{- if $pc.Simple }} code lines).
privacy:
  vimeo:
    disable: false
    enableDNT: false
    simple: true # default is false
vimeo.html
is easy to understand if you has comprehended the Instagram template.
Below @params, @return and @example-s comments are removed,
while some commented insights and blank lines are added.
{{/* Get hugo.toml configs about Vimeo's privacy */}}
{{- $pc := site.Config.Privacy.Vimeo }}
{{/* If Vimeo's privacy is not disabled */}}
{{- if not $pc.Disable }}
  {{/* If Vimeo's privacy is simple run the respective template */}}
  {{- if $pc.Simple }}
    {{- template "_shortcodes/vimeo_simple.html" . }}
  {{- else }}
    {{/* Get arguments.
    By:
      - `id` can be the 1st positional param or a named param.
      - All rest must be named params: `title`, `class`,...
    If a parameter is not passed in the shortcode call (or passed nil) then assign the respective default value to its argument.
    Otherwise assign the param value.
    Validate bool argument $allowFullScreen (and $iframeAllowList).
    Validate $id in later render block (with).
    Constant $divStyle and $iframeStyle
    --- START --- */}}
    {{- $dnt := cond $pc.EnableDNT 1 0 }} {{/* For $src */}}
    {{- $allowFullScreen := true }}
    {{- $class := or (.Get "class") }}
    {{- $id := or (.Get "id") (.Get 0) }}
    {{- $loading := or (.Get "loading") }}
    {{- $title := or (.Get "title") }}
    {{- if in (slice "true" true 1) (.Get "allowFullScreen") }}
      {{- $allowFullScreen = true }}
    {{- else if in (slice "false" false 0) (.Get "allowFullScreen") }}
      {{- $allowFullScreen = false }}
    {{- end }}
    {{- $iframeAllowList := "" }}
    {{- if $allowFullScreen }}
      {{- $iframeAllowList = "fullscreen" }}
    {{- end }}
    {{- $divStyle := "position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;" }}
    {{- $iframeStyle := "position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" }}
    {{/* --- END --- */}}
    {{/* Render <div> and <iframe>
    --- START --- */}}
    {{- with $id }} {{/* id param must be pased in the shortcode call (1st positional or named) */}}
      {{- $src := printf "https://player.vimeo.com/video/%v?dnt=%v" . $dnt }}
      <div
        {{- with $class }}
          class="{{ . }}"
        {{- else }}
          style="{{ $divStyle | safeCSS }}"
        {{- end }}>
        <iframe
          src="{{- $src }}"
          {{- if not $class }}
            style="{{ $iframeStyle | safeCSS }}"
          {{- end }}
          {{- with $iframeAllowList }} allow="{{ . }}" {{- end }}
          {{- with $loading }} loading="{{ . }}" {{- end }}
          {{- with $title }} title="{{ . }}" {{- end }}>
        </iframe>
      </div>
    {{- else }}
      {{- errorf "The %q shortcode requires a video id, either as the first positional argument or an argument named id. See %s" .Name .Position }}
    {{- end }} {{/* with $id */}}
    {{/* --- END --- */}}
  {{- end }} {{/* if not $pc.Simple */}}
{{- end }} {{/* if not $pc.Disable */}}
So much effort into understanding the shortcode template; it would be a shame if it went to waste because it wasn’t properly called. Check examples of invalid calls in the Both named and positional args section.
X - Twitter
MWE
{{< x user="SanDiegoZoo" id="1453110110599868418" >}}
Renders the X’s tweet https://x.com/SanDiegoZoo/status/1453110110599868418 to:
Owl bet you'll lose this staring contest π¦ pic.twitter.com/eJh4f2zncC
— San Diego Zoo Wildlife Alliance (@sandiegozoo) October 26, 2021
The docs:
- Explain each argument
 - How to overwrite this shortcode call to run your own shortcode template
 - And the Hugo configs related
 
Declaration
First understand the comprensive explanation of the display shortcode in previous section.
X’s shortcode template is x.html.
But if in your Hugo configs has set true to simple’s X privacy
then it runs x_simple.html
This is a little complex to understand it 100% since it take advantage of some functions:
collections.Querify, in code justquerifybecause Hugo allows omitting the namespace in templatestransform.Unmarshalresources.GetRemote
Anyhow, if you want to attempt this comprehension challenge, first read Vimeo’s template overview in the paragraph above.
YouTube
MWE
{{< youtube 0RKpf3rK57I >}}
Renders the video https://www.youtube.com/watch?v=0RKpf3rK57I to:
The docs:
- Explain each argument
 - How to overwrite this shortcode call to run your own shortcode template
 - And the Hugo configs related
 
Declaration
Youtube shortcode template is youtube.html.
Fortunately, Vimeo’s template overview in the paragraph above is analogous to YouTube’s template.
Override a default shortcode
For example the details shortcode declaration in the script details.html
might not fit your needs.
To solve this we consulted the details shortcode documentation, which says
To override Hugoβs embedded
detailsshortcode, copy the source code to a file with the same name in thelayouts/_shortcodesdirectory.
Let’s get to work.
$EDITOR layouts/_shortcodes/details.html. Note the underscore_in_shortcodes.- Paste next
 
{{- /* Get arguments. */}}
{{- $class := or (.Get "class") "table-of-contents" }}
{{- $name := or (.Get "name") "" }}
{{- $summary := or (.Get "summary") "Table of Contents" }}
{{- $open := true }}
{{- if in (slice "false" false 0) (.Get "open") }}
  {{- $open = false }}
{{- else if in (slice "true" true 1) (.Get "open") }}
  {{- $open = true }}
{{- end }}
{{- /* Render. */}}
<details
  {{- with $class }} class="{{ . }}" {{- end }}
  {{- with $name }} name="{{ . }}" {{- end }}
  {{- with $open }} open {{- end }}
>
  <summary>{{ $summary | .Page.RenderString }}</summary>
  {{ .Inner | .Page.RenderString (dict "display" "block") -}}
</details>
Quick explanation:
@param,@referenceand@examplescomments are removed for brevity- The 
title[param and argument] is removed. Now if we hover our pointer over the<describe>element we will no longer see a small pop-up text. More about this global attribute here. $summarynew default value is"Table of contents".$opennew default value istrue$classnew default value is"table-of-contents". Define it inlayouts/partials/custom_head.html. For example:
details.table-of-contents > :not(summary) {
  font-weight: bold;
}
Which means: for all direct children (see child combinator >),
except <summary>-s, of a <details> elements with class table-of-contents set the font-weight to bold.
So :not(summary) is every HTML element except <summary>.
This is valid because it’s compatible with Hugo’s .Page.RenderString method,
which is already briefly described when
.Inner is discussed in the Get values section within the Details paragraphs.
- Insert the shortcode call in a markdown, e.g., 
$EDITOR content/en/myshortcode.mdwith 
---
title: "Test Custom Details"
---
{{< details >}}
This `<p>` and `<code>` are bold
- And this `<li>` too.
- Even [links](https://gohugo.io)!
Every HTML element children of `<details>` is bold
...except `<summary>` !!
| Tables | are |
|----|---|
| not exempt | from |
| being | bold |
{{< /details >}}
- Build 
hugo server --disableFastRender - Check in the browser (http://localhost:1313/myshortcode/) that it’s rendered alike:
 
Table of Contents
This <p> and <code> are bold
- And this 
<li>too. - Even links!
 
Every HTML element children of <details> is bold
…except <summary> !!
| Tables | are | 
|---|---|
| not exempt | from | 
| being | bold | 
Hugo Scroll
Theme defined shortcodes are declared in layouts/shortcodes/:
> tree layouts/shortcodes
layouts/shortcodes
βββ contact_list.html
βββ email.html
βββ extlink.html
βββ icon.html
βββ phone.html
βββ rawhtml.html
We will not modify any of them, thus locally we can find them under themes/hugo-scroll/layouts/shortcodes/.
For details read this explanation.
Raw HTML
rawhtml.html is defined as
<!-- raw html -->
{{.Inner}}
E.g. usage to change font-family in part of a link <a> text:
{{< rawhtml >}}
  My post about Tolkien:
  <a href="/blogs/tolkien/">
  <span style="font-family: 'MiddleEarth JoannaVu', cursive;">Tolkien</span>:
  books, podcasts and much more!
  </a>
{{< /rawhtml >}}
Renders to:
My post about Tolkien: Tolkien: books, podcasts and much more!This is the simplest shortcode to use.
It uses .Inner. See Get values for more info.
Alternative, to directly write HTML in your Hugo markdowns add next to your config.toml (or equivalent in YAML/JSON).
But it’s NOT recommended.
[markup.goldmark.renderer]
  unsafe = true
Read
For simpler usage, just a block class/style attribute use markdown attributes like:
Tolkien
{class="font-middle-earth-joanna-vu"}
Tolkien
{.font-middle-earth-joanna-vu}
Tolkien
{style="font-family: 'MiddleEarth JoannaVu', 'Great Vibes', 'Open Sans', sans-serif;"}
contact_list
contact_list.html is defined as
{{ range .Site.Params.contacts }}
    <p><i class="{{ .icon }}"></i> <a href="{{ .url | safeURL }}">{{ .value }}</a></p>
{{ end }}
Site.Params access docs.
Use
{{<contact_list>}}
Renders to:
It’s almost all the content of the example site contact.md.
The theme’s params are specified in your configuration file,
mine is config.toml,
in [params] section. It starts with:
# Theme-specific variables `.Site.Params.myParamName`
[params]
    # The path to your "favicon". This should be a square (at least 32px x 32px) png-file.
    favicon = "images/favicon.png"
One can also read them via hugo config | sed -n '/^\[params\]/,/^\[permalinks\]/p':
[params]
  copyright = '...'
  description = 'Juan Marinero...'
  favicon = 'images/favicon.png'
  hidedesignbyline = true/false
  image_options = 'webp ...'
  images = ['images/cover-image.jpg']
  title = 'Juan Marinero...'
  title_guard = true
  [[params.contacts]]
    icon = 'fa fa-phone'
    label = 'phone'
    url = 'tel:...'
    value = '...'
  ...
  [params.meta]
    keywords = "Data Science, ..."
[permalinks]
Read Argument collection
Use the Params method to access the arguments as a collection.
MWE: read Access site/page parameters and Current context for pages.
Concerning the contact list, the shortcode template contact_list.html loop all via {{ range .Site.Params.contacts }}.
More about the range function in the docs.
It’s worth also checking how to access a specific element of the slice (or array) via {{ index .Site.Params.contacts 0 }} for example,
this is explained in previous section about the param shortcode, link.
email and phone
email.html is defined as
{{ .Site.Params.contact.email }}
Usage:
{{<email>}}
phone.html is analogous.
extlink
extlink.html is defined as
{{ with .Get "href" }}
  <a href="{{ . }}" target="_blank">
{{ end }}
{{ with .Get "icon" }}
  <i class="{{ . }}"></i>
{{ end }}
{{ with .Get "text" }}
  {{ . }}</a>
{{ end }}
Example of shortcode call:
{{< extlink href="https://example.com" text="Click Me" >}}
Which renders as:
<a href="https://example.com" target="_blank">Click Me</a>
i.e. to
Click MeAn optional icon can be created too:
{{< extlink href="https://example.com" icon="fas fa-external-link-alt" text="Visit Example" >}}
Renders to
<a href="https://example.com" target="_blank"><i class="fas fa-external-link-alt"></i> Visit Example</a>
And one can see: Visit Example
What about calling it without a text parameter? Or explicitly empty like this:
{{< extlink href="https://example.com" text="" >}}
Take a minute to study the highlighted code lines below.
  {{ with .Get "href" }}
    <a href="{{ . }}" target="_blank">
  {{ end }}
  {{ with .Get "icon" }}
    <i class="{{ . }}"></i>
  {{ end }}
  {{ with .Get "text" }}
    {{ . }}</a>
  {{ end }}Well, in both scenarios the {{ with .Get "text" }} will be evaluated as false,
and consequently the {{ . }}</a> won’t be rendered (with dot the text value).
This can be considered as a critical flaw,
or as a feature, since it makes both href and text mandatory parameters.
If both are mandatory lets prevent a broken link (</a> not closed) if the Hugo user forgets to pass the text parameter.
And analogous (missing opening <a> tag) if forgot to pass the href parameter.
Thus our proposal should render anything at all only if both parameters are passed.
Try to code it yourself!
Solution A - Click to expand
{{ $href := or (.Get "href") "" }}
{{ with $href }}
  {{ with .Get "text" }}
    <a href=$ref target="_blank">
    {{ with .Get "icon" }}
      <i class="{{ . }}"></i>
    {{ end }}
    {{ . }}
    </a>
  {{ end }}
{{ end }}
Solution B - Click to expand
{{ $href := .Get "href" }}
{{ $text := .Get "text" }}
{{ if and $href $text }}
  <a href="{{ $href }}" target="_blank">
    {{ with .Get "icon" }}<i class="{{ . }}"></i> {{ end }}
    {{ $text }}
  </a>
{{ else }}
  {{ errorf "extlink shortcode requires 'href' and 'text' parameters. }}
{{ end }}
icon
icon.html is defined as
{{ if .Get "brand" }}
  {{ with .Get "name" }}<i class="fa-brands fa-{{ . }}"></i>{{ end }}
{{ else }}
  {{ with .Get "name" }}<i class="fa fa-{{ . }}"></i>{{ end }}
{{ end }}
Usage example passing brand
{{< icon name="github" brand=true >}}
Which renders as:
<i class="fa-brands fa-github"></i>
i.e. to
Now with name
{{< icon name="rocket" >}}
We get
<i class="fa fa-rocket"></i>
And it’s shown:
A practical use (as tutorial) in the example site services.md:
### Icons
This theme includes the full set of [Font Awesome v6.6.0 icons](https://fontawesome.com/icons). Use the `{{< icon >}}` [shortcode](https://gohugo.io/content-management/shortcodes/) with the respective `name` to use an icon directly in your `.md` files. For example "{{< icon name="envelope" >}}":
```html
{{< icon name="envelope" >}}
```
If you want to use one of Font Awesome's brand iconsβthe ones that have a trademark warning and the `fa-brands` classβadd `brand=true`. For example "{{< icon name="github" brand=true >}}":
```html
{{< icon name="github" brand=true >}}
```