Now, let’s talk about the secret sauce, the duct tape, and the junk drawer of your Hugo site: Custom Params. This is where you get to attach your own arbitrary key-value data to pretty much anything—your site, your sections, and most importantly, your content pages. It’s the mechanism that lets you move beyond the standard front matter fields and build truly dynamic templates.

The concept is brilliantly simple. In your front matter, under a top-level key called params, you can define any custom data you want. Hugo doesn’t care what you put in there; it just collects it all and makes it available for you in your templates. It’s your personal storage locker for template variables.

The Anatomy of a Custom Param

You’ll typically define them in your content’s front matter. Let’s say you’re writing a restaurant review and want to add a star rating and a price range. Your front matter might look like this in TOML:

title = "Review: The Greasy Spoon"
date = 2023-10-25T08:44:00-04:00

[params]
rating = 4
price_range = "$"
cuisines = ["Diner", "American", "Breakfast"]
health_inspection_grade = "B" # We're being honest here.

Or, if you’re a YAML aficionado:

---
title: "Review: The Greasy Spoon"
date: 2023-10-25T08:44:00-04:00
params:
  rating: 4
  price_range: "$"
  cuisines:
    - Diner
    - American
    - Breakfast
  health_inspection_grade: B
---

See? Clean and logical. You’ve just extended the definition of a “page” in your Hugo project. Now, the real magic happens in the template.

Accessing Params in Templates

This is where you reap what you’ve sown. Accessing these values in your template is straightforward, but the path differs slightly depending on where you defined the param.

To access a page’s own params, you use .Params (note the capital ‘P’) from within the page’s context. To get our rating from the example above:

<h2>My Rating</h2>
<p>{{ .Params.rating }} out of 5 stars.</p>

Want to list the cuisines?

<p><strong>Cuisines:</strong>
{{ range .Params.cuisines }}
    <span class="tag">{{ . }}</span>
{{ end }}
</p>

You can also define site-wide params in your hugo.toml (or config.toml) file. These are available everywhere, on every page, under site.Params. This is perfect for things like your Twitter handle, an admin email, or the current year of your theme.

[params]
twitter_handle = "@mycoolsite"
company_name = "Widgets Inc."
theme_color = "salmon" # A questionable choice, but it's your site.
<footer>Follow us on Twitter: {{ site.Params.twitter_handle }}</footer>

The Quirks and “Gotchas”

Here’s the trench knowledge. Hugo’s handling of params is powerful but has a few idiosyncrasies that will bite you if you don’t know about them.

  1. Case Sensitivity: This is the big one. Hugo lowercases all your parameter keys. Always. No exceptions. So if you define myCoolParam in your front matter, you must access it as .Params.mycoolparam. It’s infuriatingly inconsistent with the rest of Hugo’s case-preserving behavior, and we all just have to live with it. Always use lowercase and underscores for your param keys. Trust me.

  2. The .Param Method: Just to keep you on your toes, Hugo also has a method .Param (singular). This is a convenience function that checks for a key first in the page’s params, and if it’s not found, it falls back to the site’s params. It’s useful for setting sensible defaults.

    {{ $twitter := .Param "twitter_handle" }} 
    <!-- This gets the page's twitter handle, or the site's if the page doesn't have one -->
    
  3. Type Checking: Remember, Hugo templates are weakly typed. Your rating param that you think is a number (4) is, as far to the template engine, often just a string (“4”). This can break comparisons. If you need to do logic like {{ if gt .Params.rating 3 }}, be safe and cast it first: {{ if gt (int .Params.rating) 3 }}. This is a classic source of silent template failures.

Best Practices: Don’t Make a Mess

The junk drawer is powerful, but you don’t want it to become a black hole of forgotten data.

  • Namespace Your Params: If you’re building a theme for distribution, prefix your custom params with a theme-specific namespace to avoid collisions. theme_, mytheme_, or an acronym is perfect. [params.mytheme.featured_color] is much safer than just [params.color].
  • Document Them: Keep a README or a commented example in your archetypes/default.md file that lists all the custom params your theme expects. Your future self, and anyone else using your theme, will thank you.
  • Use Them for Good, Not Evil: Custom params are for content-related data. If you find yourself stuffing huge blobs of HTML or complex JSON structures into them, you’re probably making a mistake. That’s what shortcodes and data files are for.

Used wisely, custom params are what transform Hugo from a simple static generator into a structured content powerhouse. They bridge the gap between your content and your presentation, letting you keep your content files clean and your templates dynamic and intelligent. Now go forth and parameterize. Just remember to use lowercase.