← all posts · hugo

Building an about page in Hugo without touching single.html

How to use Hugo's layout key to give a standalone page its own template, rather than bending a shared layout with conditionals.

02 May 2026 · 3 min read · Stephen Masters hugodevops

The temptation

You have a working layouts/_default/single.html for article pages. It renders a hero image, an eyebrow label, a date, and a comments section. Now you need an About page — same fonts, same nav, same footer, but none of that article-specific structure.

The tempting path: add a conditional.

go-html-template
1234
{{ if ne .Type "about" }}
  <div class="article-hero-image">...</div>
  <div class="eyebrow">Route report · {{ .Date.Format "January 2006" }}</div>
{{ end }}

Don’t. Every conditional you add to a shared layout is a claim that two fundamentally different things are the same thing. single.html accumulates special cases over time, and eventually you’re reading a template full of if branches trying to reconstruct which of five page types you’re on.


The layout front matter key

Hugo has a cleaner answer. Any content file can declare the template it wants:

yaml
1234
---
title: Hello.
layout: about
---

Hugo looks up layouts/_default/about.html and uses it for this page. single.html is never involved. The about page gets its own template, does exactly what it needs to do, and nothing else changes.


The layout file

go-html-template
12345678910111213141516171819202122232425
{{ define "main" }}
<div class="prose">
  <header class="about-header">
    <div class="eyebrow">About</div>
    <h1 class="about-title">{{ .Title }}</h1>
  </header>

  {{- with .Params.portrait -}}
  {{- $img := resources.Get (strings.TrimLeft "/" .src) -}}
  {{- if $img -}}
  {{- $portrait := $img.Resize "680x webp" -}}
  <figure class="about-portrait">
    <img src="{{ $portrait.RelPermalink }}"
         alt="{{ $.Params.portrait.caption | default $.Title }}"
         loading="lazy">
    {{- with $.Params.portrait.caption -}}
    <figcaption>{{ . }}</figcaption>
    {{- end -}}
  </figure>
  {{- end -}}
  {{- end }}

  {{ .Content }}
</div>
{{ end }}

{{ define "main" }} plugs into the baseof.html base template — the about page still gets the nav, footer, and any globally-loaded scripts. It just has a different main block.

The portrait image is optional. resources.Get returns nil if the file doesn’t exist; the {{ if $img }} guard means the figure is simply omitted rather than causing a build error. The page renders correctly before you have a photo to put on it.


The content file

yaml
12345678910
---
title: Hello.
description: One sentence for the HTML meta description.
layout: about
portrait:
  src: /images/about/portrait.jpg
  caption: Somewhere alongside the Loire.
---

Body text here, written in Markdown as normal.

description is kept for <meta name="description"> but the layout doesn’t render it on the page — the design goes straight from h1 to photo to prose. One field, two uses, no duplication.


When to use a dedicated layout

This pattern is worth reaching for whenever a page differs structurally from the norm rather than just in content. Good candidates:

  • An About page (no hero, no date, no comments)
  • A Contact page (a form, no article body)
  • An index page that needs a custom header or grid

If a page needs a single field suppressed, a conditional in the shared template is probably fine. If it needs multiple sections replaced or a different overall structure, give it its own layout. The distinction is: am I configuring the template, or am I fighting it?

SM
Stephen Masters

Software developer and architect. I build systems for places that move energy, commodities, and money around. I keep a bike-packing journal at velostevie.com.