Right, let’s talk about with. This is one of those template actions that seems simple until you stare into its abyss and the abyss stares back. It’s not just a fancy if statement. It’s a scope-changer. Think of it as a temporary, focused lens you hold up to a specific piece of your data. Its primary job is to say, “For everything inside this block, stop looking way over there. The thing you need is right here.”

The most common, and frankly most useful, way to use with is to safely navigate potentially nil data. It’s your guard against those pesky nil pointer dereference errors that would rather crash your entire site than render a blank. Here’s the classic, sensible use case:

{{ with .Params.author }}
    <h3>Written by: {{ . }}</h3>
{{ end }}

See what happened? Inside that with block, the dot . is no longer the grand, all-seeing page context. It’s been re-scoped, re-bound, reduced to just the value of .Params.author. If .Params.author exists and isn’t nil, the block executes and . is now that string. If it’s nil or empty, the entire block is skipped. It’s elegant. It’s safe. It’s why we use it.

The Dot-Demotion Principle

This is the core concept you must internalize. with demotes the scope. The mighty dot you had access to outside—with all its methods, page variables, site variables, and existential dread—is now gone. You’ve traded the entire universe for a single, hopefully useful, planet.

This leads to the single most common “oh crap” moment people have with with. You try to access something from the outer scope and get nothing.

<h2>{{ .Title }}</h2> <!-- This works -->
{{ with .Params.featured_image }}
    <img src="{{ . }}" alt="Featured Image"/> <!-- . is the image path -->
    <p>Image from: {{ $.Site.BaseURL }}</p> <!-- This will fail, . is now the image -->
{{ end }}

Whoops. Inside the with block, .Title is now nil because . is the featured image string, and a string doesn’t have a Title field. This is where you meet $, your emergency escape hatch. The $ always refers to the top-level, global context—the original dot you started with. The fix is simple but crucial:

{{ with .Params.featured_image }}
    <img src="{{ . }}" alt="Featured Image"/>
    <p>Image from: {{ $.Site.BaseURL }}</p> <!-- $ to the rescue! -->
{{ end }}

Truthiness and the Empty Gotcha

Here’s where the designers made a choice I find… questionable. with doesn’t just check for nil. It checks for “truthiness,” which in Go templates means any non-empty value. An empty string "", a slice of length zero [], a map with no keys {}—all of these are considered “falsey” and will cause the with block to be skipped.

This is mostly helpful, but it can bite you if you have a valid zero value you need to handle.

// Assume .Params.rating is 0, a valid integer rating.
{{ with .Params.rating }}
    <p>Rating: {{ . }}/5</p> <!-- This block will NOT render. 0 is falsey! -->
{{ end }}

Yep. A rating of 0 is a perfectly valid value, but with sees the number 0, considers it falsey, and skips the block. This is absurd, but it’s the law of the land. In cases like this, where you need to distinguish between a missing value and a zero value, you must use an if check instead. if has the same truthiness rules, but you can use else.

{{ if isset .Params "rating" }}
    <p>Rating: {{ .Params.rating }}/5</p> <!-- This will render for 0 -->
{{ else }}
    <p>Rating not set.</p>
{{ end }}

Best Practice: Chaining with else

Speaking of else, you can chain it with with for a clean “if present, do this, otherwise do that” pattern. This is incredibly useful for providing defaults.

{{ with .Params.thumbnail }}
    <img src="{{ . }}" alt="Thumbnail"/>
{{ else }}
    <img src="/images/default-thumbnail.jpg" alt="Default Thumbnail"/>
{{ end }}

Just remember: inside the else block, the dot has been reset to its original, outer value. The failed with statement doesn’t permanently change the scope; it’s only effective within its own block.

So, use with as your polite bouncer. It lets valid data through to a simplified, focused context and turns away nil or empty values that would cause problems. Just always keep one hand on the $ escape rope, and know when its truthiness rules are working against you.