The "ACF Image field returns an array but I wanted a URL" symptom comes from a single setting on the field: the Return Format. There are three options (Image Array, Image URL, Image ID) and each returns a fundamentally different shape. Pick the wrong one and your template code does not know what to do with the value. Here is what each returns, when to pick which, and the cleanest pattern for responsive output.
Jump to:
- The three return formats
- What each format actually returns
- When to pick Image Array
- When to pick Image URL
- When to pick Image ID
- The cleanest pattern for responsive images
- Changing return format on an existing field
The three return formats
In Custom Fields > Field Groups > [your field group] > [your image field] > Return Format, you see three radio buttons:
- Image Array (default for most use cases). Returns a PHP array with the URL, alt text, all the registered image sizes, dimensions, mime type, and the attachment ID.
- Image URL. Returns the URL of the full-size original image as a string.
- Image ID. Returns the WordPress attachment ID as an integer.
The default is "Image Array" in modern ACF Pro versions. This is the right default for most agency work, but it is also the cause of every "I called <img src="..."> and got [object Object] looking output" ticket.
What each format actually returns
Image Array (array):
$image = get_field( 'hero_image' );
// $image looks like:
// [
// 'ID' => 42,
// 'id' => 42,
// 'title' => 'Hero photograph',
// 'filename' => 'hero.jpg',
// 'url' => 'https://example.com/wp-content/uploads/2026/01/hero.jpg',
// 'alt' => 'Sunset over a city skyline',
// 'caption' => '',
// 'description' => '',
// 'mime_type' => 'image/jpeg',
// 'width' => 2400,
// 'height' => 1600,
// 'sizes' => [
// 'thumbnail' => 'https://example.com/.../hero-150x150.jpg',
// 'medium' => 'https://example.com/.../hero-300x200.jpg',
// 'large' => 'https://example.com/.../hero-1024x683.jpg',
// 'thumbnail-width' => 150,
// 'thumbnail-height' => 150,
// // ... per registered image size
// ],
// // ... more keys
// ]Image URL (string):
$image = get_field( 'hero_image' );
// $image looks like:
// 'https://example.com/wp-content/uploads/2026/01/hero.jpg'Image ID (int):
$image = get_field( 'hero_image' );
// $image looks like:
// 42When to pick Image Array
Use Image Array when:
- You want responsive images (different sizes for different breakpoints) without making extra database queries.
- You need the alt text (which you almost always do, for accessibility).
- You need image dimensions (for
width/heightattributes that prevent layout shift). - You want maximum flexibility in the template without re-querying the attachment.
This is the right default for 90% of agency work. The "extra data" is cached as part of the meta read; the cost is minimal.
$image = get_field( 'hero_image' );
if ( $image ) {
printf(
'<img src="%s" alt="%s" width="%d" height="%d">',
esc_url( $image['url'] ),
esc_attr( $image['alt'] ),
intval( $image['width'] ),
intval( $image['height'] )
);
}When to pick Image URL
Use Image URL when:
- You only ever need the URL (no alt, no dimensions, no sizes).
- You are passing the URL to a third-party library that wants a string.
- You want the absolute minimum payload in the template variable.
Honest take: I almost never pick this. The savings over Image Array are negligible, and you eventually want the alt text or dimensions for some reason. Default to Image Array unless you have a specific reason.
When to pick Image ID
Use Image ID when:
- You will pass the value to a WordPress function that expects an attachment ID (
wp_get_attachment_image,wp_get_attachment_image_url,wp_get_attachment_metadata). - You want the minimal storage in the database (just the ID, not the full array reference).
- You are storing many image references (a gallery with hundreds of items) and the cumulative meta cost matters.
This pairs well with wp_get_attachment_image, which produces a complete <img> tag with srcset attributes already filled in:
$image_id = get_field( 'hero_image' );
if ( $image_id ) {
echo wp_get_attachment_image( $image_id, 'large', false, [
'class' => 'hero-image',
'loading' => 'lazy',
] );
}This is the pattern I reach for on directory sites with thousands of listings, where minimizing meta size matters at scale.
The cleanest pattern for responsive images
The wp_get_attachment_image function (with the ID return format) produces a <img srcset> automatically based on the registered image sizes. This is the cleanest path to responsive images from an ACF Image field:
$image_id = get_field( 'hero_image' );
if ( $image_id ) {
echo wp_get_attachment_image( $image_id, 'large', false, [
'class' => 'hero-image w-full h-auto',
'loading' => 'eager',
'fetchpriority' => 'high',
'sizes' => '(min-width: 1280px) 1280px, 100vw',
] );
}This produces:
<img width="1024" height="683" src=".../hero-1024x683.jpg"
class="hero-image w-full h-auto"
loading="eager" fetchpriority="high"
sizes="(min-width: 1280px) 1280px, 100vw"
srcset=".../hero-300x200.jpg 300w, .../hero-768x512.jpg 768w, .../hero-1024x683.jpg 1024w, .../hero-2400x1600.jpg 2400w"
alt="Sunset over a city skyline">For agency projects I typically default to this pattern (ID return format + wp_get_attachment_image) because it gives me responsive output with one line of template code. See wp_get_attachment_image docs for full options.
For deeper coverage of responsive image patterns with ACF, see Proper Responsive Images with ACF Image Fields.
Changing return format on an existing field
If you change the Return Format on a field that already has data in production, the stored database value does not change. ACF stores the attachment ID in wp_postmeta regardless of return format; the return format only controls how get_field hydrates that ID into a value at read time. So switching formats is safe at the data layer, but you may break templates that expect the old shape.
Process for safely changing return format on a production field:
- Switch the return format in the field group admin.
- Update every template that reads this field to use the new shape.
- Test on staging.
- Deploy template changes and the field group change together.
If you do not control template code (the field is used in many templates across the codebase), it is sometimes safer to leave the existing field as-is and create a new field with the desired return format.
The honest 80/20: default to Image Array unless you have a specific reason. For high-scale sites where meta size matters, ID + wp_get_attachment_image is the cleaner pattern. For the alt-text-only case, see How to Get ALT Text from an ACF Image Field.
Sources
Authoritative references this article was fact-checked against.
- Image field (Advanced Custom Fields docs)advancedcustomfields.com
- wp_get_attachment_image() function reference (WordPress Developer Resources)developer.wordpress.org





