Hugo Shortcodes Lengkap: Membuat Components Reusable
Hugo Shortcodes Lengkap: Membuat Components Reusable
Shortcodes adalah fitur powerful di Hugo untuk membuat components reusable dalam content Markdown. Mereka memungkinkan Anda menyisipkan complex HTML/JSX dengan syntax sederhana di Markdown.
Apa itu Shortcodes?
Definisi
Shortcodes adalah snippets template yang bisa dipanggil dari content files untuk menambahkan fungsionalitas atau styling kompleks tanpa menulis HTML langsung di Markdown.
Mengapa Menggunakan Shortcodes?
- DRY Principle: Tidak mengulang kode yang sama
- Consistency: Format konsisten di seluruh site
- Simplicity: Content writers bisa menambahkan complex components
- Maintainability: Update satu file, efek global
- Security: Escape content secara otomatis
Built-in Shortcodes Hugo
1. figure
Menampilkan gambar dengan caption:
{{ < figure src="/images/photo.jpg" title="Photo Title" caption="Photo description" > }}
2. gist
Embed GitHub Gist:
{{ < gist username gist-id > }}
3. highlight
Syntax highlighting dengan options:
{{ < highlight go "linenos=table,hl_lines=2 3" > }}
package main
func main() {
fmt.Println("Hello")
}
{{ < /highlight > }}
4. instagram
Embed Instagram post:
{{ < instagram post-id > }}
5. param
Mengakses site parameters:
Site author: {{ < param author > }}
6. ref dan relref
Internal linking dengan validasi:
[Link ke post]({{ < ref "blog/my-post.md" > }})
[Relative link]({{ < relref "my-post.md" > }})
7. tweet
Embed Twitter/X tweet:
{{ < tweet user="username" id="123456789" > }}
8. vimeo dan youtube
Embed video:
{{ < vimeo video-id > }}
{{ < youtube video-id > }}
Membuat Custom Shortcodes
Struktur Folder
layouts/
└── shortcodes/
├── alert.html # Single shortcode
├── figure.html # Override built-in
├── button.html
├── gallery.html
├── code-block.html
└── youtube-custom.html
1. Alert Shortcode
File: layouts/shortcodes/alert.html
{{ $type := .Get "type" | default "info" }}
{{ $title := .Get "title" | default "" }}
{{ $colors := dict
"info" "bg-blue-50 border-blue-200 text-blue-800"
"warning" "bg-yellow-50 border-yellow-200 text-yellow-800"
"success" "bg-green-50 border-green-200 text-green-800"
"error" "bg-red-50 border-red-200 text-red-800"
"tip" "bg-purple-50 border-purple-200 text-purple-800"
}}
<div class="rounded-lg border-l-4 p-4 my-6 {{ index $colors $type }}">
{{ with $title }}
<h4 class="font-semibold mb-2">{{ . }}</h4>
{{ end }}
<div class="text-sm">
{{ .Inner | markdownify }}
</div>
</div>
Penggunaan:
< alert type="warning" title="Perhatian" >
Pastikan untuk backup data sebelum melanjutkan.
< /alert >
< alert type="tip" >
Gunakan Hugo Extended untuk fitur image processing.
< /alert >
2. Button Shortcode
File: layouts/shortcodes/button.html
{{ $url := .Get "url" | default "#" }}
{{ $text := .Get "text" | default "Click me" }}
{{ $style := .Get "style" | default "primary" }}
{{ $size := .Get "size" | default "md" }}
{{ $external := .Get "external" | default false }}
{{ $styles := dict
"primary" "bg-blue-600 text-white hover:bg-blue-700"
"secondary" "bg-gray-200 text-gray-800 hover:bg-gray-300"
"outline" "border-2 border-blue-600 text-blue-600 hover:bg-blue-50"
"danger" "bg-red-600 text-white hover:bg-red-700"
}}
{{ $sizes := dict
"sm" "px-3 py-1.5 text-sm"
"md" "px-4 py-2 text-base"
"lg" "px-6 py-3 text-lg"
}}
<a href="{{ $url }}"
class="inline-flex items-center rounded-lg font-medium transition-colors {{ index $styles $style }} {{ index $sizes $size }}"
{{ if $external }}target="_blank" rel="noopener noreferrer"{{ end }}>
{{ $text }}
{{ if $external }}
<svg class="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
</svg>
{{ end }}
</a>
Penggunaan:
{{ < button url="https://gohugo.io" text="Visit Hugo Docs" style="primary" size="lg" external="true" > }}
{{ < button url="/contact" text="Contact Us" style="outline" > }}
3. Gallery Shortcode
File: layouts/shortcodes/gallery.html
{{ $folder := .Get "folder" | default "images/gallery" }}
{{ $match := .Get "match" | default "*" }}
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 my-8">
{{ $images := .Page.Resources.Match (printf "%s/%s" $folder $match) }}
{{ range $images }}
<div class="relative aspect-square overflow-hidden rounded-lg group">
{{ $resized := .Resize "400x400" }}
<img src="{{ $resized.RelPermalink }}"
alt="{{ .Name }}"
class="w-full h-full object-cover transition-transform group-hover:scale-110"
loading="lazy">
<div class="absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-30 transition-opacity"></div>
</div>
{{ end }}
</div>
Penggunaan:
{{ < gallery folder="images/trip" match="*.jpg" > }}
4. Code Block dengan File
File: layouts/shortcodes/code-file.html
{{ $file := .Get "file" }}
{{ $lang := .Get "lang" | default "text" }}
{{ $title := .Get "title" | default $file }}
{{ $path := printf "%s" $file }}
{{ $content := readFile $path }}
<div class="code-block my-6">
<div class="flex items-center justify-between px-4 py-2 bg-gray-800 rounded-t-lg">
<span class="text-gray-300 text-sm font-mono">{{ $title }}</span>
<button onclick="navigator.clipboard.writeText(this.nextElementSibling.textContent)"
class="text-gray-400 hover:text-white text-sm">
Copy
</button>
</div>
{{ highlight $content $lang "" }}
</div>
Penggunaan:
{{ < code-file file="config.toml" lang="toml" title="hugo.toml" > }}
{{ < code-file file="layouts/_default/baseof.html" lang="html" > }}
5. YouTube Custom
File: layouts/shortcodes/youtube-custom.html
{{ $id := .Get "id" }}
{{ $title := .Get "title" | default "YouTube Video" }}
{{ $start := .Get "start" | default 0 }}
{{ $autoplay := .Get "autoplay" | default false }}
<div class="video-container my-8">
<div class="relative w-full" style="padding-bottom: 56.25%;">
<iframe
class="absolute inset-0 w-full h-full rounded-lg"
src="https://www.youtube.com/embed/{{ $id }}?start={{ $start }}{{ if $autoplay }}&autoplay=1{{ end }}"
title="{{ $title }}"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen>
</iframe>
</div>
{{ with $title }}
<p class="text-center text-sm text-gray-600 mt-2">{{ . }}</p>
{{ end }}
</div>
Penggunaan:
{{ < youtube-custom id="dQw4w9WgXcQ" title="Hugo Tutorial" start="30" > }}
6. Card Component
File: layouts/shortcodes/card.html
{{ $title := .Get "title" }}
{{ $image := .Get "image" }}
{{ $link := .Get "link" | default "#" }}
{{ $style := .Get "style" | default "default" }}
{{ $styles := dict
"default" "bg-white"
"dark" "bg-gray-800 text-white"
"primary" "bg-blue-50 border-blue-200"
}}
<a href="{{ $link }}" class="block rounded-xl shadow-md overflow-hidden hover:shadow-xl transition-shadow {{ index $styles $style }}">
{{ with $image }}
<img src="{{ . }}" alt="{{ $title }}" class="w-full h-48 object-cover">
{{ end }}
<div class="p-6">
<h3 class="text-xl font-semibold mb-2">{{ $title }}</h3>
<div class="text-gray-600">
{{ .Inner | markdownify }}
</div>
</div>
</a>
Penggunaan:
{{ < card title="Hugo Tutorial" image="/images/hugo.jpg" link="/tutorials/hugo" > }}
Learn how to build fast static sites with Hugo.
{{ < /card > }}
Parameter Handling
Positional vs Named Parameters
Positional (bisa semua):
<!-- shortcode: {{ < myshortcode param1 param2 > }} -->
{{ $first := .Get 0 }}
{{ $second := .Get 1 }}
Named (lebih readable):
<!-- shortcode: {{ < myshortcode key="value" > }} -->
{{ $value := .Get "key" }}
Mixed:
<!-- {{ < myshortcode "value1" key="value2" > }} -->
{{ $first := .Get 0 }}
{{ $second := .Get "key" }}
Default Values
{{ $title := .Get "title" | default "Untitled" }}
{{ $show := .Get "show" | default true }}
{{ $count := .Get "count" | default 5 }}
Boolean Parameters
{{ $external := false }}
{{ if eq (.Get "external") "true" }}
{{ $external = true }}
{{ end }}
Inner Content
Simple Shortcodes
{{ < shortcode param="value" > }}
Paired Shortcodes
{{ < shortcode > }}
Inner content here with **markdown** support.
{{ < /shortcode > }}
Template:
<div class="box">
{{ .Inner | markdownify }}
</div>
Conditional Inner Content
{{ with .Inner }}
<div class="content">
{{ . | markdownify }}
</div>
{{ else }}
<p class="placeholder">No content provided</p>
{{ end }}
Advanced Shortcodes
1. Tabs Component
File: layouts/shortcodes/tabs.html
<div class="tabs my-6" x-data="{ activeTab: 0 }">
<div class="flex border-b border-gray-200">
{{ $tabs := split (.Inner) "<!-- tab -->" }}
{{ range $index, $tab := after 1 $tabs }}
{{ $parts := split $tab "<!-- /tab -->" }}
{{ $title := index (split (index $parts 0) " ") 0 }}
<button
@click="activeTab = {{ $index }}"
:class="{ 'border-blue-500 text-blue-600': activeTab === {{ $index }} }"
class="px-4 py-2 font-medium text-gray-500 hover:text-gray-700 border-b-2 border-transparent">
{{ $title }}
</button>
{{ end }}
</div>
<div class="py-4">
{{ range $index, $tab := after 1 $tabs }}
{{ $parts := split $tab "<!-- /tab -->" }}
{{ $content := index $parts 0 }}
<div x-show="activeTab === {{ $index }}" class="prose">
{{ $content | markdownify }}
</div>
{{ end }}
</div>
</div>
Penggunaan:
{{ < tabs > }}
<!-- tab macOS -->
### Installation on macOS
```bash
brew install hugo
Installation on Windows
choco install hugo-extended
Installation on Linux
sudo apt install hugo
{{ < /tabs > }}
### 2. Details/Summary (Accordion)
**File: `layouts/shortcodes/details.html`**
```html
{{ $summary := .Get "summary" | default "Details" }}
{{ $open := .Get "open" | default false }}
<details class="my-4 p-4 bg-gray-50 rounded-lg" {{ if $open }}open{{ end }}>
<summary class="font-semibold cursor-pointer">{{ $summary }}</summary>
<div class="mt-4 prose">
{{ .Inner | markdownify }}
</div>
</details>
Penggunaan:
{{ < details summary="Click to expand" open="true" > }}
Hidden content that can be toggled.
{{ < /details > }}
3. Book Rating
File: layouts/shortcodes/book.html
{{ $title := .Get "title" }}
{{ $author := .Get "author" }}
{{ $rating := .Get "rating" | default 0 }}
{{ $cover := .Get "cover" }}
{{ $link := .Get "link" }}
<div class="flex gap-4 my-6 p-4 bg-gray-50 rounded-lg">
{{ with $cover }}
<img src="{{ . }}" alt="{{ $title }}" class="w-24 h-36 object-cover rounded shadow">
{{ end }}
<div class="flex-1">
<h3 class="font-bold text-lg">{{ $title }}</h3>
<p class="text-gray-600">{{ $author }}</p>
<div class="flex items-center gap-1 mt-2">
{{ range seq 5 }}
{{ if le . $rating }}
<svg class="w-5 h-5 text-yellow-400 fill-current" viewBox="0 0 20 20">
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"></path>
</svg>
{{ else }}
<svg class="w-5 h-5 text-gray-300 fill-current" viewBox="0 0 20 20">
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"></path>
</svg>
{{ end }}
{{ end }}
</div>
{{ with $link }}
<a href="{{ . }}" class="text-blue-600 hover:underline mt-2 inline-block">View on Amazon →</a>
{{ end }}
</div>
</div>
Penggunaan:
{{ < book title="Atomic Habits" author="James Clear" rating="5" cover="/images/atomic-habits.jpg" link="https://amazon.com/..." > }}
Shortcode Security
XSS Prevention
<!-- Selalu escape output -->
{{ $title := .Get "title" | htmlEscape }}
<!-- Atau gunakan safeHTML dengan hati-hati -->
{{ $html := .Get "html" | safeHTML }}
Markdownify vs HTML
<!-- Use markdownify untuk inner content -->
{{ .Inner | markdownify }}
<!-- Use .RenderString untuk custom markdown -->
{{ .RenderString .Inner }}
Best Practices
1. Naming Conventions
- Descriptive names:
youtube-customlebih baik dariyt - Consistent naming:
alert,button,card - Avoid conflicts: Jangan override built-in kecuali perlu
2. Documentation
Tambahkan docs di shortcode file:
<!--
Usage:
{{ < alert type="warning" title="Title" > }}
Content here
{{ < /alert > }}
Parameters:
- type: info|warning|success|error|tip (default: info)
- title: string (optional)
-->
3. Default Values
Selalu berikan default untuk required parameters:
{{ $title := .Get "title" | default "Untitled" }}
{{ $style := .Get "style" | default "default" }}
4. Validation
Check parameters sebelum digunakan:
{{ if .Get "title" }}
<!-- Render dengan title -->
{{ else }}
{{ errorf "Shortcode 'card': missing required parameter 'title'" }}
{{ end }}
Kesimpulan
Shortcodes adalah fitur powerful Hugo untuk:
✅ Reusability: Components sekali buat, pakai berulang kali
✅ Consistency: Format uniform di seluruh site
✅ Simplicity: Markdown tetap bersih dan readable
✅ Flexibility: Kustomisasi tanpa batas
✅ Maintainability: Update global dengan edit satu file
Gunakan shortcodes untuk:
- Complex layouts
- Embeds (YouTube, CodePen, dll)
- Styled components (alerts, buttons, cards)
- Content variations (tabs, accordions)
Artikel Terkait
Link Postingan : https://www.tirinfo.com/hugo-shortcodes-lengkap/