4.5 The .Site Object in Templates: Accessing Configuration
Alright, let’s talk about the .Site object. This is your golden ticket, your backstage pass to all the global configuration and metadata you so cleverly defined in your hugo.toml (or hugo.yaml, or hugo.json—I don’t judge). Think of it as the Grand Central Station for your site’s data. It’s how your templates get access to everything from your site’s title to your custom parameters.
While you can access some of this stuff via the global site variable (note the lowercase), .Site is the more fully-featured, canonical way to do it, especially within a template’s scope. They often point to the same underlying data, but I stick with .Site because it’s explicit and you’ll see it in 99% of the examples out there.
The Core Configuration Trio
The most common things you’ll pull from .Site are your site-wide configs. You set these in your config file, and you retrieve them in your templates. It’s a beautiful, if simple, cycle.
<title>{{ .Site.Title }}</title>
<meta name="description" content="{{ .Site.Params.description }}">
<link rel="canonical" href="{{ .Site.BaseURL }}">
<footer>
<p>© {{ now.Year }} {{ .Site.Copyright }}</p>
</footer>
See? Straightforward. .Site.Title, .Site.BaseURL, and .Site.Copyright map directly to the title, baseURL, and copyright keys in your config file. The .Site.Params object is where things get more interesting; it’s a catch-all bucket for your custom configuration, which we’ll dive into next. But first, a pro tip: always use {{ .Site.BaseURL }} for your absolute URLs instead of hardcoding your domain. The first time you deploy to a staging environment and your CSS breaks because you hardcoded https://myawesomeblog.com, you’ll understand why. I’ve been there. It’s not a fun debugging session.
Diving into .Site.Params for Custom Configuration
This is where Hugo shines. The [params] section in your config file is your personal playground. You can define anything your heart desires and access it site-wide. Let’s say you want to add a GitHub handle and a default Twitter card image.
In your hugo.toml:
baseURL = 'https://example.com/'
languageCode = 'en-us'
title = 'My Incredible Site'
[params]
description = "A site that's actually incredible, unlike most."
github = "yourusername"
defaultImage = "/images/twitter-card.png"
In your template, you access these not directly on .Site, but on the .Site.Params object:
<a href="https://github.com/{{ .Site.Params.github }}">
Follow me on GitHub
</a>
<meta name="twitter:image" content="{{ .Site.BaseURL }}{{ .Site.Params.defaultImage }}">
Notice the subtle but crucial detail in that last line? .Site.Params.defaultImage is just a string path. To make it a full, absolute URL, you have to concatenate it with .Site.BaseURL. Forgetting that .BaseURL is the number one cause of broken images when you’re new to this. The browser isn’t psychic; it doesn’t know your site’s root domain unless you tell it.
Environment-Aware Configuration with .Site.IsServer
This is a killer feature. Hugo lets you run a local development server with hugo server. Often, you want behavior in development that you don’t want in production. For example, maybe you want to load a different analytics script, or prevent search indexing.
You can check the current environment using .Site.IsServer. This boolean will be true when you’re running the local server and false during a regular hugo build.
<head>
{{ if .Site.IsServer }}
<!-- Load a development/script-tag-manager-thingy -->
<script src="/js/dev-analytics.js"></script>
{{ else }}
<!-- Load the real, terrifying production analytics -->
<script async src="https://real-analytics.com/script.js"></script>
{{ end }}
</head>
You can get more granular with other environment variables, but .Site.IsServer is the 80% solution. Use it to keep your development junk out of your production builds. It’s like the difference between wearing sweatpants at home and a suit at a wedding—context matters.
A Word of Caution: The .Site.RegularPages Gotcha
Here’s a classic “trap for young players” that still gets experienced devs. You might see .Site.RegularPages and think, “Great! A list of all my pages!” And you’d be right. But what you might not realize is that this list includes pages from all languages and all sections. It’s the entire site’s worth of content.
If you try to use this in a heavily customized list.html template, expecting it to be the pages for the current section only, you’re going to have a bad time. For that, you want the current context’s .Pages or .RegularPages. .Site.RegularPages is global. Use it for site-wide feeds, recent posts widgets, or sitemaps—not for section-specific listings. The distinction is everything. Always ask yourself: “Do I need all the things, or just the things from here?” Your template logic will thank you.