Hugo Partials: Building Reusable Template Components
Hugo partials are reusable template fragments — the building blocks of a maintainable Hugo theme. Any piece of HTML that appears in more than one place belongs in a partial: site headers, footers, navigation menus, article cards, SEO meta tags, social sharing blocks, author bios. Once defined, a partial is called with a single line from any template.
This guide covers the full range of how partials work and patterns that make them practical for a publishing site.
Basic Partial Usage
Partials live in layouts/partials/. A partial file is a standard Go HTML template. Call it from any layout:
{{ partial "header.html" . }}
The . passes the current page context to the partial. The partial has full access to page variables, site variables, and any data passed along.
Building a Header Partial
layouts/partials/header.html:
<header class="site-header">
<div class="container">
<a href="{{ .Site.BaseURL }}" class="site-title">
{{ .Site.Title }}
</a>
<nav class="main-nav">
{{ range .Site.Menus.main }}
<a href="{{ .URL }}"
class="nav-link{{ if $.IsMenuCurrent "main" . }} nav-link--active{{ end }}">
{{ .Name }}
</a>
{{ end }}
</nav>
</div>
</header>
Note the $ prefix in $.IsMenuCurrent — inside a range loop, . refers to the current loop item. $ always refers to the root page context, so $.IsMenuCurrent checks the active menu state against the current page correctly.
Passing Custom Context
Instead of passing . (the full page context), you can pass a custom data structure — a dict — to a partial:
{{ partial "article-card.html" (dict
"Title" .Title
"URL" .RelPermalink
"Date" .Date
"Summary" .Summary
"Tags" .Params.tags
"Image" .Params.featured_image) }}
Inside layouts/partials/article-card.html, access the passed data directly:
<article class="article-card">
{{ with .Image }}
<div class="card-image">
<img src="{{ . }}" alt="{{ $.Title }}">
</div>
{{ end }}
<div class="card-body">
<h2 class="card-title">
<a href="{{ .URL }}">{{ .Title }}</a>
</h2>
<time class="card-date">{{ .Date.Format "January 2, 2006" }}</time>
<p class="card-summary">{{ .Summary }}</p>
{{ with .Tags }}
<div class="card-tags">
{{ range . }}
<a href="/tags/{{ . | urlize }}/" class="tag">{{ . }}</a>
{{ end }}
</div>
{{ end }}
</div>
</article>
Passing explicit context rather than the full page . keeps partials reusable — the same article card partial works called from a list template, a related posts block, or a search results page.
SEO Meta Partial
A practical SEO partial that handles the common cases cleanly:
layouts/partials/seo.html:
{{- $title := .Title -}}
{{- $description := .Params.description | default .Summary | default .Site.Params.description -}}
{{- $image := .Params.featured_image | default .Site.Params.og_image -}}
{{- $url := .Permalink -}}
<meta name="description" content="{{ $description | truncate 160 }}">
<!-- Open Graph -->
<meta property="og:title" content="{{ $title }}">
<meta property="og:description" content="{{ $description | truncate 200 }}">
<meta property="og:url" content="{{ $url }}">
<meta property="og:type" content="{{ if .IsPage }}article{{ else }}website{{ end }}">
{{ with $image }}<meta property="og:image" content="{{ . | absURL }}">{{ end }}
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ $title }}">
<meta name="twitter:description" content="{{ $description | truncate 200 }}">
{{ with $image }}<meta name="twitter:image" content="{{ . | absURL }}">{{ end }}
<!-- Article-specific -->
{{ if .IsPage }}
{{ with .Date }}<meta property="article:published_time" content="{{ .Format "2006-01-02T15:04:05Z07:00" }}">{{ end }}
{{ with .Lastmod }}<meta property="article:modified_time" content="{{ .Format "2006-01-02T15:04:05Z07:00" }}">{{ end }}
{{ range .Params.tags }}<meta property="article:tag" content="{{ . }}">{{ end }}
{{ end }}
Called from the base layout head:
<head>
<title>{{ .Title }} | {{ .Site.Title }}</title>
{{ partial "seo.html" . }}
</head>
Cached Partials
For partials that are computationally expensive and do not change per-page, use partialCached:
{{ partialCached "sidebar.html" . }}
Hugo renders the partial once and reuses the output for every subsequent call with the same context. For a sidebar that lists recent posts or popular tags — data that is the same on every page — this meaningfully reduces build time on large sites.
You can cache on a custom key to create multiple distinct cached versions:
{{ partialCached "sidebar.html" . .Section }}
This caches one version per section, so the sidebar can vary by section while still being cached within each section.
Inline Partials
For a partial used only once in a specific template, define it inline with define:
{{ define "partials/local-callout" }}
<div class="callout">
<p>{{ .message }}</p>
</div>
{{ end }}
<!-- Use it later in the same file -->
{{ template "partials/local-callout" (dict "message" "This only appears here") }}
Inline partials keep locally-relevant template logic in the file that uses it rather than creating a partials file for something not intended for reuse.
Organizing Partials
For sites with many partials, organize them in subdirectories:
layouts/partials/
components/
article-card.html
author-bio.html
tag-list.html
pagination.html
sections/
header.html
footer.html
sidebar.html
seo/
meta.html
json-ld.html
open-graph.html
Call them with the subdirectory path:
{{ partial "components/article-card.html" . }}
{{ partial "seo/meta.html" . }}
JSON-LD Structured Data Partial
A structured data partial for article pages:
layouts/partials/seo/json-ld.html:
{{ if .IsPage }}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": {{ .Title | jsonify }},
"datePublished": "{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}",
"dateModified": "{{ .Lastmod.Format "2006-01-02T15:04:05Z07:00" }}",
"author": {
"@type": "Organization",
"name": {{ .Site.Title | jsonify }}
},
"publisher": {
"@type": "Organization",
"name": {{ .Site.Title | jsonify }},
"url": {{ .Site.BaseURL | jsonify }}
},
"description": {{ .Summary | jsonify }},
"url": {{ .Permalink | jsonify }}
}
</script>
{{ end }}
Partials are where the maintainability of a Hugo theme lives. A theme built with well-structured, single-purpose partials is straightforward to update, debug, and extend. A theme that puts everything in a few large layout files becomes difficult to work with as the site grows.