An ACF Image field returns an array, URL string, or attachment ID according to its Return Format setting. For ordinary responsive markup, I use an ID with wp_get_attachment_image(). For a custom template that needs several attachment properties, an array is convenient.
All three formats refer to the same stored attachment ID. Changing the return format changes the value your template receives; it does not shrink the stored field or migrate the media library.
The three return formats
| Return format | PHP value | Useful when |
|---|---|---|
| Image Array | Array containing ID, url, alt, dimensions and sizes | A component needs several attachment properties |
| Image URL | URL string | A library specifically requires a URL |
| Image ID | Integer attachment ID | WordPress should build the image markup |
Always handle an empty field. If PHP prints “Array”, the template is using an array where it expected a string; JavaScript may instead show [object Object] for an object converted to text.
What each format actually returns
// Image Array:
$image = get_field( 'hero_image' );
if ( is_array( $image ) ) {
echo wp_get_attachment_image( (int) $image['ID'], 'large' );
}
// Image URL:
$url = get_field( 'decorative_texture' );
if ( is_string( $url ) && '' !== $url ) {
printf( '<img src="%s" alt="">', esc_url( $url ) );
}
// Image ID:
$id = get_field( 'card_image' );
if ( $id ) {
echo wp_get_attachment_image( (int) $id, 'medium' );
}These are alternatives for fields configured with the corresponding formats. The URL example deliberately represents a decorative image. Do not copy empty alt text onto an informative image without considering its purpose.
An array commonly includes ID, url, alt, width, height, and a sizes map. A named size provides URL and dimension entries, such as sizes['medium'] and sizes['medium-width']. Alt text belongs to the attachment, not separately to each generated size.
Alt text and empty-alt handling
For an array, use $image['alt']; for an ID, use the attachment metadata:
$alt = (string) get_post_meta( $image_id, '_wp_attachment_image_alt', true );
printf( '<img src="%s" alt="%s">', esc_url( $url ), esc_attr( $alt ) );For a known ACF Image field, I can obtain the unformatted stored ID regardless of its configured return format:
$image_id = (int) get_field( 'hero_image', get_the_ID(), false );
if ( $image_id ) {
echo wp_get_attachment_image( $image_id, 'large' );
}For legacy code that has only a URL, attachment_url_to_postid($url) is a fallback. It may return zero for a transformed CDN URL, a resized filename, an external image, or a URL that no longer matches stored attachment data. Prefer retaining the ID rather than doing URL lookups throughout a listing loop.
An empty alt="" is correct for decorative images. Informative images need a meaningful text alternative appropriate to where they appear. A media-library description may be a useful default, but the same image can serve different purposes in different components. For a linked image, explain the destination or function when nearby link text does not already do so. I do not automatically substitute the filename, caption, or article title for missing alt text; those can be redundant or misleading. Send missing informative alternatives back to an editor.
The cleanest pattern for responsive images
Set the field to Image ID and let WordPress use the attachment's registered image sizes:
$hero_id = (int) get_field( 'hero_image' );
if ( $hero_id ) {
echo wp_get_attachment_image( $hero_id, 'large', false, array(
'class' => 'hero-image',
'loading' => 'eager',
'fetchpriority' => 'high',
'sizes' => '(min-width: 1280px) 1280px, 100vw',
) );
}This example assumes the hero is the likely Largest Contentful Paint image. Reserve high priority for that important image instead of assigning it to every image above the fold. For lower-page images, let WordPress's normal loading heuristics operate or explicitly use lazy loading when appropriate.
WordPress supplies intrinsic dimensions, attachment alt text and responsive candidates when suitable metadata exists. It does not place every registered size in srcset: candidates must satisfy its size and aspect-ratio checks. The sizes value must describe the image's actual CSS width; change the 1280px assumption if your layout differs.
Register the image sizes you actually want
add_action( 'after_setup_theme', function () {
add_image_size( 'te-hero-mobile', 768, 432, true );
add_image_size( 'te-hero-desktop', 1920, 1080, true );
add_image_size( 'te-card', 480, 320, true );
} );The fourth argument selects cropping. WordPress does not generally upscale a small upload to satisfy a larger size. Newly registered sizes affect subsequent image processing; regenerate the relevant sizes for existing attachments when needed:
wp media regenerate 42 --image_size=te-hero-desktop --yesCheck available disk space, staging output and backup policy before regenerating a whole library. Registration alone does not prove a derivative exists.
The manual srcset pattern
For custom markup, use the helper-produced candidate list and the actual selected dimensions:
$id = (int) get_field( 'hero_image', get_the_ID(), false );
$image = $id ? wp_get_attachment_image_src( $id, 'large' ) : false;
if ( $image ) {
$srcset = wp_get_attachment_image_srcset( $id, 'large' );
$alt = (string) get_post_meta( $id, '_wp_attachment_image_alt', true );
printf(
'<img src="%s" width="%d" height="%d" alt="%s"%s sizes="(min-width: 1280px) 1280px, 100vw">',
esc_url( $image[0] ), (int) $image[1], (int) $image[2], esc_attr( $alt ),
$srcset ? ' srcset="' . esc_attr( $srcset ) . '"' : ''
);
}The browser selects a candidate using layout width, device pixel ratio and its own loading decisions. Check currentSrc in browser developer tools at several viewport widths; a long srcset string alone does not prove efficient delivery.
The picture element for art direction
Use separate fields when the editor must choose genuinely different crops or compositions, not merely smaller files of one image:
$desktop_id = (int) get_field( 'hero_desktop', get_the_ID(), false );
$mobile_id = (int) get_field( 'hero_mobile', get_the_ID(), false );
$mobile = $mobile_id ? wp_get_attachment_image_src( $mobile_id, 'large' ) : false;
if ( $desktop_id ) : ?>
<picture>
<?php if ( $mobile ) : ?>
<source media="(max-width: 767px)"
srcset="<?php echo esc_url( $mobile[0] ); ?>"
width="<?php echo (int) $mobile[1]; ?>"
height="<?php echo (int) $mobile[2]; ?>">
<?php endif; ?>
<?php echo wp_get_attachment_image( $desktop_id, 'large', false, array(
'loading' => 'eager',
'fetchpriority' => 'high',
) ); ?>
</picture>
<?php endif;Give both crops the same informative purpose so the fallback image's text alternative works for either. Account for differing aspect ratios in CSS and verify layout shift on the browsers you support. If the mobile field is empty, the desktop image remains the fallback.
Cloudflare image transforms as a delivery layer
If image transformations are enabled for your Cloudflare zone, a transformation URL can generate delivery variants:
<img src="https://images.example.com/cdn-cgi/image/width=1280,format=auto,quality=85/hero.jpg"
srcset="https://images.example.com/cdn-cgi/image/width=768,format=auto/hero.jpg 768w,
https://images.example.com/cdn-cgi/image/width=1280,format=auto/hero.jpg 1280w"
sizes="(min-width: 1280px) 1280px, 100vw"
width="1280" height="720" alt="Describe the image's purpose here">This is an optional delivery setup, not a property of storing a file in R2 or putting a hostname behind Cloudflare. Confirm the zone configuration, source access, actual output dimensions, format and caching. Derive candidate URLs from trusted attachment URLs; do not expose an unrestricted fetch proxy. WordPress's normal responsive output remains a useful default without this dependency.
Changing return format on an existing field
The stored ID stays the same, but a template expecting $image['url'] will break if get_field() begins returning an integer. Search for every field read, shortcode, REST consumer and component before changing the setting. Update code and the field definition together on staging, test empty fields and existing uploads, then deploy both in the same release.
For a transition, the unformatted-ID pattern above can normalize a known Image field without depending on its return setting. If a field's name or type also changes, treat that as a separate data migration.
Check the rendered src, currentSrc, dimensions, alt text, empty-field behavior and LCP loading choice. For a nested image component, the same rendering helpers work inside the Flexible Content template-part pattern; for slow image-heavy lists, profile ACF and attachment reads before changing storage.
Sources
Authoritative references this article was fact-checked against.
- ACF Image fieldadvancedcustomfields.com
- ACF raw and formatted valuesadvancedcustomfields.com
- ACF image field implementationgithub.com
- WordPress attachment image outputdeveloper.wordpress.org
- Register image sizesdeveloper.wordpress.org
- Responsive candidate selectiondeveloper.wordpress.org
- W3C alt-text decision treew3.org
- Cloudflare image transformation featuresdevelopers.cloudflare.com





