Hugo

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.