Tutorial Hugo Partials: Membuat Reusable Template Components
Tutorial Hugo Partials: Membuat Reusable Template Components
Partials adalah komponen reusable dalam Hugo templates yang memungkinkan Anda memecah template kompleks menjadi bagian-bagian yang manageable dan reusable.
Apa itu Partials?
Definisi
Partials adalah template snippets yang bisa dipanggil dari template lain menggunakan fungsi partial. Mereka adalah building blocks untuk membangun layouts yang kompleks.
Mengapa Menggunakan Partials?
- DRY Principle: Tulis sekali, pakai di banyak tempat
- Modularity: Template lebih mudah di-maintain
- Reusability: Komponen bisa dipakai di berbagai layout
- Caching: Partials bisa di-cache untuk performa
- Testing: Lebih mudah test komponen individual
Struktur Partials
Directory Structure
layouts/
└── partials/
├── head.html # HTML head section
├── header.html # Site header
├── footer.html # Site footer
├── navigation.html # Navigation menu
├── sidebar.html # Sidebar content
├── pagination.html # Pagination
├── seo-meta.html # SEO meta tags
├── schema-jsonld.html # Schema.org JSON-LD
├── opengraph.html # Open Graph tags
├── twitter-card.html # Twitter Card meta
├── analytics.html # Analytics scripts
├── comments.html # Comments section
├── related-posts.html # Related posts
├── breadcrumbs.html # Breadcrumb navigation
├── toc.html # Table of Contents
├── social-share.html # Social sharing buttons
├── author-box.html # Author bio box
├── newsletter.html # Newsletter signup
├── search.html # Search form
├── svg/
│ ├── icon-facebook.html
│ ├── icon-twitter.html
│ └── icon-github.html
└── cards/
├── post-card.html
└── author-card.html
Basic Partials
1. Head Partial
File: layouts/partials/head.html
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} | {{ .Site.Title }}{{ end }}</title>
<!-- CSS -->
{{ $css := resources.Get "css/main.css" }}
{{ $css = $css | resources.PostCSS }}
{{ if hugo.IsProduction }}
{{ $css = $css | resources.Minify | resources.Fingerprint }}
<link rel="stylesheet" href="{{ $css.RelPermalink }}" integrity="{{ $css.Data.Integrity }}">
{{ else }}
<link rel="stylesheet" href="{{ $css.RelPermalink }}">
{{ end }}
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<!-- Preconnect -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<!-- Canonical -->
<link rel="canonical" href="{{ .Permalink }}">
<!-- RSS -->
{{ range .AlternativeOutputFormats }}
<link rel="{{ .Rel }}" type="{{ .MediaType.Type }}" href="{{ .Permalink }}" title="{{ $.Site.Title }}">
{{ end }}
Penggunaan di baseof.html:
<!DOCTYPE html>
<html lang="{{ .Site.LanguageCode }}">
<head>
{{ partial "head.html" . }}
</head>
<body>
{{ block "main" . }}{{ end }}
</body>
</html>
2. Header Partial
File: layouts/partials/header.html
<header class="bg-white shadow-sm sticky top-0 z-50">
<nav class="container mx-auto px-4">
<div class="flex justify-between items-center h-16">
<!-- Logo -->
<a href="{{ .Site.Home.RelPermalink }}" class="text-xl font-bold text-gray-900">
{{ .Site.Title }}
</a>
<!-- Desktop Navigation -->
<div class="hidden md:flex items-center space-x-8">
{{ range .Site.Menus.main }}
<a href="{{ .URL }}" class="text-gray-600 hover:text-gray-900 transition-colors">
{{ .Name }}
</a>
{{ end }}
<!-- Search Toggle -->
<button id="search-toggle" class="p-2 text-gray-600 hover:text-gray-900">
{{ partial "svg/icon-search.html" . }}
</button>
</div>
<!-- Mobile Menu Button -->
<button id="mobile-menu-btn" class="md:hidden p-2">
{{ partial "svg/icon-menu.html" . }}
</button>
</div>
</nav>
<!-- Mobile Menu (hidden by default) -->
<div id="mobile-menu" class="hidden md:hidden bg-white border-t">
{{ range .Site.Menus.main }}
<a href="{{ .URL }}" class="block px-4 py-3 text-gray-600 hover:bg-gray-50">
{{ .Name }}
</a>
{{ end }}
</div>
</header>
3. Footer Partial
File: layouts/partials/footer.html
<footer class="bg-gray-900 text-white py-12">
<div class="container mx-auto px-4">
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<!-- Brand -->
<div>
<h3 class="text-lg font-semibold mb-4">{{ .Site.Title }}</h3>
<p class="text-gray-400">{{ .Site.Params.description }}</p>
</div>
<!-- Quick Links -->
<div>
<h4 class="font-semibold mb-4">Menu</h4>
<ul class="space-y-2">
{{ range .Site.Menus.footer }}
<li>
<a href="{{ .URL }}" class="text-gray-400 hover:text-white transition-colors">
{{ .Name }}
</a>
</li>
{{ end }}
</ul>
</div>
<!-- Social -->
<div>
<h4 class="font-semibold mb-4">Follow Us</h4>
<div class="flex space-x-4">
{{ range .Site.Params.social }}
<a href="{{ .url }}" class="text-gray-400 hover:text-white transition-colors" aria-label="{{ .name }}">
{{ partial (printf "svg/icon-%s.html" .icon) . }}
</a>
{{ end }}
</div>
</div>
</div>
<div class="mt-8 pt-8 border-t border-gray-800 text-center text-gray-400">
<p>© {{ now.Year }} {{ .Site.Title }}. All rights reserved.</p>
</div>
</div>
</footer>
Partials dengan Parameters
Passing Parameters
Cara 1: Single Parameter (Context)
<!-- Panggil partial dengan context -->
{{ partial "card.html" . }}
<!-- Atau dengan context berbeda -->
{{ partial "card.html" $post }}
Cara 2: Multiple Parameters (Dict)
<!-- Passing multiple parameters -->
{{ partial "card.html" (dict "title" "My Title" "image" "/img.jpg" "link" "/page") }}
4. Card Partial dengan Parameters
File: layouts/partials/cards/post-card.html
{{ $title := .title }}
{{ $image := .image }}
{{ $link := .link }}
{{ $date := .date }}
{{ $summary := .summary }}
{{ $categories := .categories }}
<article class="bg-white rounded-lg shadow-sm overflow-hidden hover:shadow-md transition-shadow">
{{ with $image }}
<a href="{{ $link }}" class="block">
<img src="{{ . }}" alt="{{ $title }}" class="w-full h-48 object-cover">
</a>
{{ end }}
<div class="p-6">
{{ with $categories }}
<div class="flex gap-2 mb-3">
{{ range . }}
<span class="text-xs font-medium text-blue-600 bg-blue-50 px-2 py-1 rounded">
{{ . }}
</span>
{{ end }}
</div>
{{ end }}
<h3 class="text-xl font-semibold mb-2">
<a href="{{ $link }}" class="text-gray-900 hover:text-blue-600 transition-colors">
{{ $title }}
</a>
</h3>
{{ with $date }}
<time class="text-sm text-gray-500 mb-3 block" datetime="{{ . }}">
{{ dateFormat "January 2, 2006" . }}
</time>
{{ end }}
{{ with $summary }}
<p class="text-gray-600 line-clamp-3">{{ . }}</p>
{{ end }}
</div>
</article>
Penggunaan:
{{ range .Pages }}
{{ partial "cards/post-card.html" (dict
"title" .Title
"image" .Params.image
"link" .RelPermalink
"date" .Date
"summary" (.Description | default .Summary)
"categories" .Params.categories
) }}
{{ end }}
5. SEO Meta Partial
File: layouts/partials/seo-meta.html
{{ $title := .title | default .Site.Title }}
{{ $description := .description | default .Site.Params.description }}
{{ $image := .image | default .Site.Params.defaultImage }}
{{ $url := .url | default .Permalink }}
{{ $type := .type | default "website" }}
<!-- Basic Meta -->
<meta name="description" content="{{ $description }}">
<meta name="author" content="{{ .Site.Params.author }}">
<!-- Open Graph -->
<meta property="og:title" content="{{ $title }}">
<meta property="og:description" content="{{ $description }}">
<meta property="og:type" content="{{ $type }}">
<meta property="og:url" content="{{ $url }}">
<meta property="og:image" content="{{ $image | absURL }}">
<meta property="og:site_name" content="{{ .Site.Title }}">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ $title }}">
<meta name="twitter:description" content="{{ $description }}">
<meta name="twitter:image" content="{{ $image | absURL }}">
{{ with .Site.Params.twitter }}
<meta name="twitter:site" content="@{{ . }}">
{{ end }}
Penggunaan:
<!-- Homepage -->
{{ partial "seo-meta.html" (dict
"title" .Site.Title
"description" .Site.Params.description
"type" "website"
) }}
<!-- Article -->
{{ partial "seo-meta.html" (dict
"title" .Title
"description" (.Description | default .Summary)
"image" .Params.image
"type" "article"
) }}
Conditional Partials
6. Breadcrumbs dengan Kondisi
File: layouts/partials/breadcrumbs.html
{{ if not .IsHome }}
<nav aria-label="Breadcrumb" class="py-4">
<ol class="flex items-center space-x-2 text-sm text-gray-600">
<li>
<a href="{{ .Site.Home.RelPermalink }}" class="hover:text-gray-900">Home</a>
</li>
{{ if .Section }}
<li>
<svg class="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
</svg>
</li>
<li>
<a href="/{{ .Section }}" class="hover:text-gray-900 capitalize">{{ .Section }}</a>
</li>
{{ end }}
{{ if and .Parent (ne .Parent .Site.Home) }}
<li>
<svg class="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
</svg>
</li>
<li>
<a href="{{ .Parent.RelPermalink }}" class="hover:text-gray-900">{{ .Parent.Title }}</a>
</li>
{{ end }}
<li>
<svg class="w-4 h-4 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path>
</svg>
</li>
<li class="text-gray-900 font-medium" aria-current="page">
{{ .Title }}
</li>
</ol>
</nav>
{{ end }}
Partial Caching
Caching untuk Performa
<!-- Tanpa cache - diproses setiap kali dipanggil -->
{{ partial "navigation.html" . }}
<!-- Dengan cache - diproses sekali dan di-cache -->
{{ partialCached "navigation.html" . .RelPermalink }}
When to Use Caching
Use partialCached untuk:
- Navigation menus
- Footer content
- Sidebars
- Social links
- Analytics scripts
Don’t cache untuk:
- Content spesifik per page
- Dynamic data
- Time-sensitive information
7. Cached Navigation
{{ partialCached "navigation.html" . .Site.Language.Lang }}
Cache key berdasarkan language untuk multilingual sites.
Advanced Patterns
8. Partial dengan Return Value
Partials bisa return values dengan return statement (Hugo 0.143+):
File: layouts/partials/functions/format-date.html
{{ $format := .format | default "January 2, 2006" }}
{{ $date := .date }}
{{ return $date.Format $format }}
Penggunaan:
{{ $formatted := partial "functions/format-date.html" (dict "date" .Date "format" "Jan 2, 2006") }}
<time>{{ $formatted }}</time>
9. Partial untuk SVG Icons
File: layouts/partials/svg/icon-github.html
<svg class="{{ .class | default "w-5 h-5" }}" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd"></path>
</svg>
Penggunaan:
{{ partial "svg/icon-github.html" (dict "class" "w-6 h-6 text-gray-600") }}
Best Practices
1. Naming Conventions
- Descriptive:
post-card.htmllebih baik daricard.html - Consistent: Gunakan prefix untuk grouping
- Kebab-case:
my-partial.html
2. Parameter Validation
{{ if not .title }}
{{ errorf "Partial 'card' requires 'title' parameter" }}
{{ end }}
3. Default Values
{{ $class := .class | default "bg-white" }}
{{ $showImage := .showImage | default true }}
4. Documentation
<!--
Partial: cards/post-card.html
Parameters:
- title (required): Post title
- image (optional): Featured image URL
- link (required): Post URL
- date (optional): Publish date
- summary (optional): Post summary
- categories (optional): Array of categories
Usage:
{{ partial "cards/post-card.html" (dict "title" .Title "link" .RelPermalink) }}
-->
5. Scope Management
<!-- Inside partial, use . untuk akses parameter -->
{{ $title := .title }}
<!-- Untuk akses global site data -->
{{ $site := site }}
{{ $site.Title }}
Common Partials Library
Analytics Partial
{{ if hugo.IsProduction }}
<!-- Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id={{ .Site.Params.googleAnalytics }}"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '{{ .Site.Params.googleAnalytics }}');
</script>
{{ end }}
Comments Partial
{{ if and .Site.Params.disqus (not .Site.IsServer) }}
<div id="disqus_thread"></div>
<script>
var disqus_config = function () {
this.page.url = "{{ .Permalink }}";
this.page.identifier = "{{ .RelPermalink }}";
};
</script>
{{ end }}
Table of Contents Partial
{{ if .TableOfContents }}
<aside class="toc">
<h3>Table of Contents</h3>
{{ .TableOfContents }}
</aside>
{{ end }}
Kesimpulan
Hugo partials adalah tools powerful untuk:
✅ Modularity: Break down templates kompleks
✅ Reusability: Komponen dipakai berulang kali
✅ Maintainability: Update sekali, efek global
✅ Performance: Caching untuk speed
✅ Organization: Struktur template yang bersih
Gunakan partials untuk semua komponen reusable dan nikmati workflow development yang lebih efisien.
Artikel Terkait
Link Postingan : https://www.tirinfo.com/tutorial-hugo-partials/