Right, so you’ve been building up your partials, making them nice and reusable, and then you hit a wall. You need a snippet of logic to not just output some HTML, but to actually give you back a value—a string, a slice, a boolean, something you can use in the parent template’s logic. You’ve probably tried the old trick of using .Scratch.Set and felt a little dirty about it. I don’t blame you. It worked, but it was clunky and indirect, like passing a note through three friends to ask someone out.

Welcome to Hugo 0.111 and its return statement for partials. This is a game-changer. It finally lets you treat your partials like the proper, self-contained functions they always should have been. You call them, they do some work, and they hand you back a result. It’s elegant, it’s clear, and it’s about time.

The Basic Syntax: It’s Just return

The syntax is exactly what you’d hope for. Inside a partial, you use the return statement. The moment Hugo hits that line, the partial stops executing and spits that value back to the caller. Here’s the simplest possible example:

Let’s create a partial that returns a specific config value, say, the site title. Save this as layouts/partials/get/site-title.html:

{{ return site.Title }}

Yes, it’s that simple. One line. Now, to call it and use the returned value, you use the partial function, but now it’s on the right-hand side of an assignment or operation:

{{ $titleFromPartial := partial "get/site-title.html" }}
<h1>Welcome to {{ $titleFromPartial }}</h1>

The key difference here is psychological: you’re not including the partial’s output; you’re calling it and storing its return value in a variable ($titleFromPartial). This shifts your entire mindset from “templating” to “scripting,” which is infinitely more powerful.

Why This Beats .Scratch Every Time

The old way involved mutating a shared object, which is prone to errors and incredibly confusing to read six months later.

The Old .Scratch Way (Don’t Do This Anymore):

{{/* In the parent template */}}
{{ partial "helpers/get-featured-image.html" . }}

{{/* Inside the partial 'helpers/get-featured-image.html' */}}
{{ .Scratch.Set "featuredImage" .Params.hero_image }}
{{/* ... and nothing is explicitly returned */}}

{{/* Back in the parent template, hoping the partial was called already */}}
{{ $image := .Scratch.Get "featuredImage" }}

This is terrible. It’s implicit. The connection between the call and the result is not obvious. Did the partial set the scratch value? Under what key? Did something else overwrite it? It’s a recipe for bugs.

The New return Way (Do This):

{{/* In the parent template */}}
{{ $image := partial "helpers/get-featured-image.html" . }}

{{/* Inside the partial 'helpers/get-featured-image.html' */}}
{{ return .Params.hero_image }}

Clean, obvious, and bulletproof. The relationship is direct and undeniable. The partial has one job: to return the featured image. The parent template has one job: to call the partial and use its return value. This is how code should work.

Passing Context and Using Arguments

You’ll notice I passed . as the second argument to the partial. This is crucial. That second argument is the context you’re sending into the partial. Without it, the partial would only have access to its own, isolated scope. You need to pass in the page context (or whatever data you’re working on) for the partial to be useful.

Let’s build a more complex partial that actually uses logic. This one checks if a page has any images in its content.

{{/* layouts/partials/utils/has-images.html */}}
{{ $page := . }}
{{ $content := $page.Content }}
{{ $images := findRE `(<img [^>]+>)` $content }}

{{ if $images }}
  {{ return true }}
{{ else }}
  {{ return false }}
{{ end }}

Now you can use this in a list template to add a visual indicator next to pages with images:

{{ range .Pages }}
  <li>
    <a href="{{ .Permalink }}">{{ .Title }}</a>
    {{ if partial "utils/has-images.html" . }}
      <span class="icon">🖼️</span>
  {{ end }}
  </li>
{{ end }}

The One Big “Gotcha”: No HTML, Ever

Here’s the most important rule, and the one you’re most likely to run into: You cannot return and capture a template fragment that contains HTML. The return value must be a simple data type: string, number, boolean, slice, map, etc.

This will not work as you expect:

{{/* This returns the string "<strong>nope</strong>", not a bold 'nope' */}}
{{ return "<strong>nope</strong>" }}

And this is a cardinal sin that will break your build:

{{/* This will cause a template compilation error */}}
{{ $html := "<strong>" | safeHTML }}
{{ return $html }}

The return function is for data, not for rendered markup. If you need to return a reusable chunk of HTML, you’re not looking for a partial with a return value; you’re looking for a classic, included partial that outputs directly. Use the right tool for the job. This limitation is actually a feature—it forces a clean separation of concerns between logic (calculating values) and presentation (outputting HTML). Embrace it.