Statamic Antlers Tutorial

The Antlers Gotcha That Puts Your Image URL as Text Above Your Image

Interloper Digital 4 min read

You're building out a page in Statamic. You've got your hero image field set up, the asset uploaded, everything looks right in the Control Panel.

Then you check the frontend and there's your image... with its full URL printed as text directly above it.

https://example.com/assets/hero-banner.png
[actual image renders here]

What in the world?


What's Happening

If you're coming from Blade, Twig, or basically any other templating engine, you probably wrote something like this:

{{ hero_image }}
    <img src="{{ url }}" alt="{{ alt }}">
{{ /hero_image }}

Looks reasonable. Tag pair syntax, access the inner properties. This is how you'd loop through things in most templating systems.

The problem is that {{ hero_image }} on its own line outputs the asset's string representation—which is its URL—before the template enters the tag pair context.

So you get:

  1. The URL as text (from {{ hero_image }})
  2. Then your properly formatted image (from the <img> tag inside)

I call this "URL bleed" and it's one of those Antlers quirks that trips up basically everyone who didn't grow up with Statamic.


The Fix: Colon Notation

For single asset fields (anything with max_files: 1 in the blueprint), skip the tag pair entirely. Use colon notation to access properties directly:

{{ if hero_image }}
    <img
        src="{{ hero_image:url }}"
        alt="{{ hero_image:alt ?? 'Default alt text' }}"
        width="{{ hero_image:width }}"
        height="{{ hero_image:height }}"
    >
{{ /if }}

No URL bleed. No weirdness. The {{ if hero_image }} check handles cases where the field is empty, and you get direct access to all the asset properties.


When Tag Pairs Are Actually Correct

Tag pair syntax works perfectly fine—and is the right choice—when you're dealing with multiple assets:

{{ gallery_images }}
    <img src="{{ url }}" alt="{{ alt }}" class="gallery-item">
{{ /gallery_images }}

When there are multiple items to loop through, the tag pair creates a proper iteration context. No URL bleed issue.

The gotcha specifically hits single assets because Antlers treats {{ hero_image }} as "output this thing" before it sees the closing tag.


A Complete Example

Here's a production-ready hero section pattern that handles everything correctly:

<section class="hero relative overflow-hidden">
    {{ if hero_image }}
        <img
            src="{{ glide:hero_image width="1920" height="1080" fit="crop" quality="85" }}"
            alt="{{ hero_image:alt ?? title }}"
            width="{{ hero_image:width }}"
            height="{{ hero_image:height }}"
            class="absolute inset-0 w-full h-full object-cover"
            loading="eager"
        >
    {{ /if }}
    <div class="relative z-10 container mx-auto px-4 py-24">
        <h1>{{ title }}</h1>
        {{ if subtitle }}
            <p class="text-xl">{{ subtitle }}</p>
        {{ /if }}
    </div>
</section>

Note the Glide integration—you can still use image manipulation with colon notation by referencing the field in the Glide tag directly.


Available Properties

When you're using colon notation, here's what you have access to:

Property What It Returns
url Public URL to the asset
path Path within the container
alt Alt text from metadata
width Image width in pixels
height Image height in pixels
size File size in bytes
size_kb File size in kilobytes
extension File extension
mime_type MIME type
focus Focal point coordinates
ratio Aspect ratio

Quick Reference

Scenario Pattern
Single asset URL {{ hero_image:url }}
Single asset with conditional {{ if hero_image }}{{ hero_image:url }}{{ /if }}
Multiple assets {{ gallery }}{{ url }}{{ /gallery }}
Single asset + Glide {{ glide:hero_image width="800" }}

How to Check Your Blueprint

Not sure if a field is single or multiple? Check the blueprint:

# Single asset - use colon notation
handle: hero_image
field:
  type: assets
  container: assets
  max_files: 1
# Multiple assets - use tag pair
handle: gallery
field:
  type: assets
  container: assets
  # No max_files or max_files > 1

This is one of those "once you know it, you never forget it" situations. The URL bleed is annoying enough that you'll remember to use colon notation for single assets going forward.

The Statamic docs do cover this, but when you're moving fast and pulling from other templating experience, tag pair syntax just feels natural. Now you know why it doesn't work the way you'd expect.

Need Help With Statamic?

Book a free Discovery Audit and get expert guidance on your Statamic challenges.

Book a Discovery Audit