Tips・WordPress custom fields (ACF / SCF)

How to display images with ACF

How to output ACF (SCF) image fields in your theme. Explains why the return value (ID, URL or array) changes the code you write, plus the one recommended line that gives you alt text and responsive images, with code.

With ACF (SCF) image fields, the field’s “Return Value” setting (ID, URL or array) changes how you write your template. Copy and paste code without knowing that, and you end up with “Array” on the page or an empty alt attribute. The bottom line: set the return value to “Image ID” and hand it to wp_get_attachment_image()—that’s the easiest route, and it handles alt text and responsive images for you.

TipsACF or SCF—which one should I install?Still deciding between ACF and SCF? Start here

First check: the three “return values” of an image field

The field editing screen (“Field Groups” → the field in question) has a “Return Value” setting. What you choose there changes what get_field('photo') gives back.

Return value settingWhat get_field() returnsWhen it fits
Image IDA number like 42Recommended. You want a function to build an img tag with alt and srcset
Image URLhttps://example.com/wp-content/uploads/...jpgYou just need it in a src attribute or a CSS background
Image ArrayAn array holding url, alt, sizes and moreYou want fine control over width, height, caption and so on

None of them is “the correct one”—the trick is to pick the format that suits the code you’re about to write. When you take over an existing site, check the settings screen first to see which one it uses.

Set the return value to Image ID and your template is essentially one line.

<?php echo wp_get_attachment_image(get_field('photo'), 'large'); ?>

That alone outputs an img tag like this.

<img width="1024" height="683" src="…-1024x683.jpg" class="attachment-large size-large"
     alt="A long-tailed tit perched on a snowy branch" loading="lazy" decoding="async"
     srcset="…-300x200.jpg 300w, …-768x512.jpg 768w, …-1024x683.jpg 1024w" sizes="…">
  • alt attribute—the “alternative text” from the media library goes in automatically
  • srcset / sizes—the right size for the screen width is chosen automatically (responsive)
  • loading=“lazy”—loading is deferred until the image comes into view

Build the <img> yourself and you have to write all of that by hand. Receiving an ID is how you choose to let WordPress handle it.

To add a class, pass an array as the fourth argument.

<?php echo wp_get_attachment_image(get_field('photo'), 'large', false, ['class' => 'product-photo']); ?>

When you receive a URL

If the return value is Image URL, you can print it directly with the_field(). This is the shortest form.

<img src="<?php the_field('photo'); ?>" alt="">

But you’re now writing the alt text yourself, so if the content is predictable, either add another field for it or use the post title as below.

<img src="<?php echo esc_url(get_field('photo')); ?>" alt="<?php the_title_attribute(); ?>">

The URL format is handy when you want a CSS background image.

<div class="hero" style="background-image: url('<?php echo esc_url(get_field('hero')); ?>');">

When you receive an array

If the return value is an Image Array, receive it into a variable with get_field() and pull out the keys. Its strength is that width, height and caption are all available.

<?php $img = get_field('photo'); ?>
<?php if ($img) : ?>
  <figure>
    <img src="<?php echo esc_url($img['sizes']['medium']); ?>"
         width="<?php echo esc_attr($img['sizes']['medium-width']); ?>"
         height="<?php echo esc_attr($img['sizes']['medium-height']); ?>"
         alt="<?php echo esc_attr($img['alt']); ?>">
    <?php if ($img['caption']) : ?>
      <figcaption><?php echo esc_html($img['caption']); ?></figcaption>
    <?php endif; ?>
  </figure>
<?php endif; ?>

These four keys are the ones you’ll use most.

  • $img['url']—the URL of the original size
  • $img['alt']—the alternative text
  • $img['caption']—the caption
  • $img['sizes']['medium']—the URL of the medium size (thumbnail, large and others follow the same shape)

Even when you receive an array, passing $img['ID'] to wp_get_attachment_image() still builds a tag with srcset. “Details from the array, tag generation from the function” is a handy combination.

Keeping the layout intact when there’s no image

An image field can be empty—and a template that doesn’t account for that breaks its layout on any post where the image was forgotten. Wrapping it in an if is all it takes.

<?php if ($id = get_field('photo')) : ?>
  <?php echo wp_get_attachment_image($id, 'large'); ?>
<?php else : ?>
  <img src="<?php echo esc_url(get_template_directory_uri() . '/images/no-image.png'); ?>" alt="">
<?php endif; ?>

$id = get_field('photo') is the “assign it and test whether it’s empty at the same time” idiom. It fits on one line, so you’ll see it a lot.

Images inside a repeater field

Inside a repeater you use get_sub_field(), not get_field(). This one is easy to get wrong.

<?php if (have_rows('gallery')) : ?>
  <ul class="gallery">
    <?php while (have_rows('gallery')) : the_row(); ?>
      <li><?php echo wp_get_attachment_image(get_sub_field('image'), 'medium'); ?></li>
    <?php endwhile; ?>
  </ul>
<?php endif; ?>
TipsHow to output an ACF repeater fieldWant the details on outputting repeater fields? Here you go

Summary

  1. Image fields change how you write code depending on the return value (ID, URL, array)—check the settings screen first
  2. Receiving an ID and using wp_get_attachment_image() is the shortest path. Alt, srcset and lazy loading all come along automatically
  3. URL suits src and background images; the array suits captions, widths and heights
  4. Always include an if for the empty case. Inside a repeater, use get_sub_field()

Images carry a lot of visual weight, so cutting corners on alt text and sizes shows up directly as a difference in quality. Let the functions do the parts they can.

FAQ

My ACF image field displays "Array".
This happens when you pass a field whose return format is "Image Array" straight to the_field(). An array can't be printed as a string, so PHP outputs the word "Array". Either receive it with get_field() and pull out something like $img['url'], or change the field's return value to "Image URL" or "Image ID".
Where does the image's alt attribute come from?
Set the return value to "Image ID" and use wp_get_attachment_image(); the alternative text you entered in the media library automatically becomes the alt attribute. When the return value is an array, it's in $img['alt'].
Can I display a thumbnail or medium size?
Yes. With an ID, use wp_get_attachment_image($id, 'medium'); with an array, name the size like $img['sizes']['medium']. 'thumbnail', 'medium', 'large' and 'full' are available as standard.
Posts without an image break my layout.
Only output when there's a value, using an if statement. The standard approach is to write if ($id = get_field('photo')) and show a fallback image in the else branch.