25.2 resources.Get and resources.GetRemote
Right, let’s talk about getting stuff. In Hugo, your content isn’t just the markdown files you lovingly craft; it’s also every image, PDF, CSS file, and JSON blob your site needs. This is where resources.Get and resources.GetRemote come in. Think of them as your two best friends for asset acquisition: one for the local stuff you’ve already committed to your project (resources.Get), and one for the brave new world of the internet (resources.GetRemote). They’re the foundation of Hugo Pipes, and once you get them, you can make this static site generator do backflips.
The key thing to understand is that these functions don’t just return a file path or a URL string. That would be too simple, and Hugo prefers a bit of ceremony. Instead, they return a Resource object (or nil if something goes horribly wrong). This object is your golden ticket. You can pipe it (|) into other Hugo functions to resize images, minify CSS, transpile JS, or even parse it as data. It’s the starting point for almost all the cool, dynamic asset processing you came here for.
resources.Get: Your Local File Fetcher
Use resources.Get when the file is sitting right there in your assets directory. Not static, mind you. assets. This is a crucial distinction. Files in static are copied directly to public untouched—Hugo’s grumpy old uncle who doesn’t want to be processed. Files in assets are the ones that get invited to the processing party.
{{/* Grabbing a local image to resize */}}
{{ with resources.Get "images/hero.jpg" }}
{{ $resized := .Resize "1200x" }}
<img src="{{ $resized.RelPermalink }}" alt="My resized hero image">
{{ else }}
{{ errorf "Oh for crying out loud, where is images/hero.jpg? Did you put it in assets/?" }}
{{ end }}
See that with/else block? That’s your best practice right there. Always, always check if resources.Get returns a value. If the file isn’t found, it returns nil, and trying to call .Resize on nil will make Hugo throw a tantrum and fail your build. The errorf function is a fantastic way to scream into the build log exactly what went wrong.
resources.GetRemote: The Internet, Now with More Caveats
When you need a file from somewhere else—a CDN, an API, another site—you summon resources.GetRemote. It’s incredibly powerful, but with great power comes great responsibility and a few annoying footnotes.
{{ $remoteData := resources.GetRemote "https://api.example.com/endpoint.json" }}
{{ with $remoteData }}
{{/* Optional: Check for errors in the HTTP response */}}
{{ if .Err }}
{{ errorf "Failed to get remote resource: %s" .Err }}
{{ else }}
{{ $data := . | transform.Unmarshal }}
<h2>Here's your data: {{ $data.title }}</h2>
{{ end }}
{{ else }}
{{ errorf "The GetRemote operation failed completely (e.g., likely a network or timeout issue)" }}
{{ end }}
Ah, the .Err field. This is Hugo’s slightly awkward way of handling HTTP errors. A 404 Not Found won’t cause resources.GetRemote to return nil; instead, it will return a Resource object with its .Err property set. So you have to check for both a complete failure (the with block) and an HTTP error (the if .Err block). It’s a bit verbose, but it’s what we’ve got.
Caching: The Good, The Bad, and The Immutable
Here’s the genius part: Hugo caches remote resources by default. It stores them in its resource cache (typically in your cache directory) and only re-fetches them if the remote file changes (based on the ETag or Last-Modified header). For a static site build, this is brilliant. It means you’re not hammering that API every single time you run hugo server.
But sometimes you want to hammer it. For development, you might want fresh data on every refresh. You can bust the cache with the % force directive. It’s as melodramatic as it sounds.
{{ $freshData := resources.GetRemote "https://api.example.com/endpoint.json" (dict "key" "value") | resources.Cache }}
<!-- Now, change the request slightly to bust the cache -->
{{ $freshData = resources.GetRemote "https://api.example.com/endpoint.json" (dict "key" "differentValue") }}
You can also pass headers, which is essential for APIs that require authentication.
{{ $headers := dict "Authorization" (printf "Bearer %s" (getenv "API_TOKEN")) }}
{{ $data := resources.GetRemote "https://api.example.com/private.json" (dict "headers" $headers) }}
The Rough Edges (Because I Have To Be Honest)
- Error Handling is Clunky: We already covered the double-check for remote errors. It’s not elegant.
- Timeouts are Opaque: If a remote server is slow,
resources.GetRemotemight just hang and then fail mysteriously. You have limited control over timeouts. - The
assetsFolder Dogma: It trips everyone up. You will, at some point, spend ten minutes debugging whyresources.Get "image.png"is returningnilbefore you remember you put it instaticand notassets. I’ve done it. You’ll do it. We all do it.
So, there you have it. resources.Get for your own stuff, resources.GetRemote for everyone else’s, and a healthy dose of error checking for both. Master these, and you’ve unlocked the first major door to what makes Hugo more than just a boring old static site generator.