Right, let’s talk about finding your stuff. You’ve dumped a bunch of images, CSS, and JavaScript files into your assets directory, and Hugo has faithfully processed them. Now you need to pull them into your templates. This is where .Resources gets off the bench, but it’s not as straightforward as you might hope. Hugo, in its infinite wisdom, decided that slapping a simple resources/images/cat.jpg path into your template would be too easy. Instead, we have a slightly more arcane, but ultimately more powerful, system.

Think of .Resources as a big, disorganized box of Lego. You know the piece you need is in there, but you have to rummage around to find it. The two main tools for this rummaging are .Match and .Get. One is for finding a bunch of things, the other is for grabbing one specific thing.

The Indiscriminate Grab: .Resources.Match

Use .Resources.Match when you want to find all resources that match a specific pattern, or glob. It’s your “find all the 2x4 red bricks” command. This is incredibly useful for generating image galleries, loading all CSS files from a specific directory, or bundling scripts.

{{ with .Resources.Match "gallery/*.jpg" }}
  <div class="gallery">
    {{ range . }}
      <img src="{{ .RelPermalink }}" alt="{{ .Name }}" width="{{ .Width }}">
    {{ end }}
  </div>
{{ end }}

Here’s the breakdown: The pattern "gallery/*.jpg" tells Hugo to look inside the Resources box and find every file that lives in a gallery subdirectory and has a .jpg extension. The with block is a nice, safe way to say “if we found anything, then…” The range loop then goes through each found resource, which is a full-fledged image object with properties like .Name (the filename), .RelPermalink (the relative URL), and .Width.

Why it’s this way: This pattern-based approach is powerful because your content defines what’s available, not your template. You can add 50 new images to the gallery/ folder, run hugo, and boom—your template automatically includes them all without you touching a line of HTML.

Pitfall: The pattern is relative to the assets directory, but it feels like it should be relative to the page bundle sometimes. It’s not. Always think from the root of assets. Also, .Match returns a slice (an array), even if it only finds one item. You can’t directly call .RelPermalink on the result of .Match; you have to range over it or index into it first (index (.Resources.Match "cat.jpg") 0).

The Precision Tool: .Resources.Get

While .Match is for finding groups, .Resources.Get is for when you know exactly which single piece you need. You want the transparent 1x1 round tile with the hole in it. This is what you use for that hero image, a favicon, or a specific script.

{{ $hero := .Resources.Get "images/hero-background.jpg" }}
{{ if $hero }}
  <div class="hero" style="background-image: url('{{ $hero.RelPermalink }}');">
{{ else }}
  {{ warnf "Could not find hero image for %s" .Page.File.Path }}
  <div class="hero no-image">
{{ end }}

Notice the if $hero check. This is non-negotiable. If you typo the path or the image doesn’t exist, $hero will be nil and trying to call .RelPermalink on it will break your entire build with a heart-stopping “nil pointer” error. The warnf function is a fantastic trick—it’ll print a warning to your console during build so you know something’s missing, but it won’t stop the site from generating.

Why it’s this way: The designers wanted this to be explicit. A missing resource is often a critical failure, so they force you to handle the possibility. It’s annoying until the first time it saves you from deploying a broken site.

When Patterns Are a Pain: Get vs. Match

Here’s the genuinely absurd part that gets everyone: the argument you pass to .Get is just a string, but the argument to .Match is a pattern, a glob. This leads to a classic “wait, what?” moment.

// This works with .Get
{{ $image := .Resources.Get "cat.jpg" }}

// This WILL NOT WORK with .Get. It silently returns nil.
{{ $image := .Resources.Get "*.jpg" }}

// For that, you MUST use .Match
{{ $images := .Resources.Match "*.jpg" }}

It’s a clear and questionable choice. .Get doesn’t take a pattern, it takes a literal path. There’s no warning, no error, it just gives you nothing. It’s the template equivalent of asking a librarian for “any book by Tolkien” and them staring at you in silence because you didn’t provide a specific title. You must use .Match for patterns. Memorize this; it will save you hours of confused debugging.

Best Practices from the Trenches

  1. Always Check for nil: I said it before, but it’s worth another line. {{ if $resource }} is your best friend. Pair it with warnf or errorf for debugging.
  2. Cache Your Resources: Hugo’s resources calls are not free. If you’re using a value more than once, assign it to a variable with $ to avoid reprocessing.
    {{ $css := resources.Get "css/main.scss" | toCSS | minify | fingerprint }}
    <link rel="stylesheet" href="{{ $css.RelPermalink }}" integrity="{{ $css.Data.Integrity }}">
    
  3. Page Bundles are Your Friend: Remember, these methods work on the global resources object (site.Resource or resources.Get) but they really shine when used inside a page bundle. Placing your images in the same directory as your index.md and using *.Resources.Get keeps everything neatly colocated and portable.

This system feels fussy at first, but once you get the hang of the Get/Match distinction, you realize it’s one of those pieces of Hugo that is actually designed correctly. It gives you the precision you need for important assets and the flexibility to grab whole categories of content, all while forcing you to write robust, error-resistant templates. And that’s something we can all appreciate, even if the syntax makes us sigh first.