ACF Image Field Renderer — Alt Text Fallback PHP

A production-ready image renderer for ACF Image fields — responsive <img> tags with a four-level alt text fallback chain, srcset generation from registered image sizes, lazy loading, decoding async, and full attribute escaping. One function call replaces scattered get_sub_field() image markup across your entire theme.

PHP component Production-tested ~3 min setup Theme helper Image renderer WP 6.0+ · ACF 6.0+ Updated May 15, 2026
TL;DR

This ACF image field renderer PHP snippet outputs responsive image markup from ACF Image fields with alt fallback, srcset support, lazy loading, and escaping.

Use this when your theme renders ACF Image fields in flexible content layouts — hero images, card thumbnails, gallery grids — and you want consistent alt text with a smart fallback chain, responsive srcset/sizes attributes, and proper escaping without repeating the same <img> tag markup in every template.

01 — Building it step by step

ACF image field renderer — The Code

This ACF image field renderer snippet renders an ACF image field with automatic alt text fallback when no alt is set in ACF.

We’ll build this in 5 small steps. Each step adds one new idea — starting from raw get_sub_field() calls scattered across your flexible content layouts, up to a complete image renderer that handles alt text, responsive sizing, lazy loading, and full attribute escaping.

Step 1: The problem. This is what most themes look like — a raw get_sub_field( 'hero_image' ) followed by manual wp_get_attachment_image_src() and an inline <img> tag in every flexible layout. Alt text is pulled from attachment meta in one line with no fallback. No srcset. No loading="lazy". After twelve layouts, you have twelve slightly different <img> blocks and no single place to add responsive attributes.

template-parts/flexible/step-1-raw.php
php
// ACF image field renderer — production-tested PHP snippet by Mosharaf Hossain
<?php
// BAD: Raw get_sub_field() in a flexible content layout.
// No alt fallback chain. No srcset. No lazy loading.
// Repeated across 12+ layouts with slight variations.

$image_id = get_sub_field( 'hero_image' ); // ACF field type: Image (Image ID)

if ( $image_id ) :
    $img = wp_get_attachment_image_src( $image_id, 'large' ); ?>
    <img src="<?php echo esc_url( $img[0] ); ?>"
         alt="<?php echo esc_attr( get_post_meta( $image_id, '_wp_attachment_image_alt', true ) ); ?>" />
<?php endif;

Step 2: A basic wrapper. theme_render_image() accepts an image ID and a registered size name and returns a complete <img> tag — or an empty string if the attachment doesn’t exist. Alt text comes from WordPress attachment meta via get_post_meta(..., '_wp_attachment_image_alt', true), cast to string so it’s never null. Width and height attributes are included from the image source array so the browser can reserve space before the image loads — no more layout shift.

template-parts/flexible/step-2-wrapper.php
php
// ACF image field renderer
<?php
/**
 * Step 2 — Basic wrapper with consistent alt retrieval.
 *
 * New: theme_render_image() accepts image ID + size.
 * Alt comes from WordPress attachment meta — no custom ACF alt yet.
 */
function theme_render_image( int $image_id, string $size = 'large' ): string {
    $img = wp_get_attachment_image_src( $image_id, $size );
    if ( ! $img ) return '';

    $alt = (string) get_post_meta( $image_id, '_wp_attachment_image_alt', true );

    return sprintf(
        '<img src="%s" alt="%s" width="%s" height="%s" />',
        esc_url( $img[0] ), esc_attr( $alt ),
        esc_attr( $img[1] ?? '' ), esc_attr( $img[2] ?? '' )
    );
}

Step 3: The full alt text fallback chain. The basic wrapper only checks WordPress attachment alt — one source. Real projects need a chain. This step adds four levels:

  1. ACF custom alt field — if the developer created a separate Text field on the attachment for hand-crafted alt text (this is the most intentional, so it comes first)
  2. WordPress attachment alt meta — the standard alt text field in the Media Library
  3. Attachment post title — the title of the attachment post as a descriptive fallback (better than an empty alt attribute)
  4. Empty string — never null, which would trigger a PHP warning when passed to esc_attr() in PHP 8.1+

The chain uses empty() checks so if any level returns an empty string (''), the next level fires. If all four levels return empty, the alt attribute is an empty string — which is valid HTML and intentional for purely decorative images.

template-parts/flexible/step-3-alt-chain.php
php
// ACF image field renderer
<?php
/**
 * Step 3 — Full alt text fallback chain (4 levels).
 *
 * New fallback order:
 *   1. ACF custom alt text field (hand-crafted by the editor)
 *   2. WordPress attachment alt meta (Media Library alt field)
 *   3. Attachment post title (descriptive last resort)
 *   4. Empty string (never null, zero PHP warnings)
 */
function theme_render_image( int $image_id, string $size = 'large', string $acf_alt_field = '' ): string {
    $img = wp_get_attachment_image_src( $image_id, $size );
    if ( ! $img ) return '';

    $alt = '';
    if ( $acf_alt_field ) {
        $alt = (string) get_field( $acf_alt_field, $image_id ); // ACF field type: Text — custom alt override
    }
    if ( empty( $alt ) ) {
        $alt = (string) get_post_meta( $image_id, '_wp_attachment_image_alt', true );
    }
    if ( empty( $alt ) ) {
        $alt = (string) get_the_title( $image_id );
    }

    return sprintf(
        '<img src="%s" alt="%s" width="%s" height="%s" />',
        esc_url( $img[0] ), esc_attr( $alt ),
        esc_attr( $img[1] ?? '' ), esc_attr( $img[2] ?? '' )
    );
}

Step 4: Responsive srcset from registered image sizes. Modern themes register multiple image sizes — thumbnail, medium, large, and custom ones like hero or card. Without srcset, every device downloads the same image regardless of screen width. wp_get_attachment_image_srcset() generates a srcset string listing all registered sizes and their closest matches for the requested size. wp_get_attachment_image_sizes() outputs the sizes attribute — a media-query string that tells the browser which slot width to expect at each breakpoint. Together, they let the browser pick the smallest image that fits the viewport.

template-parts/flexible/step-4-srcset.php
php
// ACF image field renderer
<?php
/**
 * Step 4 — Responsive: srcset from registered WordPress sizes.
 *
 * New: wp_get_attachment_image_srcset() generates a srcset
 * from all registered image sizes. wp_get_attachment_image_sizes()
 * outputs the sizes attribute so browsers pick the right variant.
 * ACF field type comments are preserved on all get_field() calls.
 */
function theme_render_image( int $image_id, string $size = 'large', string $acf_alt_field = '' ): string {
    $img = wp_get_attachment_image_src( $image_id, $size );
    if ( ! $img ) return '';

    $srcset = wp_get_attachment_image_srcset( $image_id, $size );
    $sizes  = wp_get_attachment_image_sizes( $image_id, $size );

    $alt = '';
    if ( $acf_alt_field ) {
        $alt = (string) get_field( $acf_alt_field, $image_id ); // ACF field type: Text — custom alt override
    }
    if ( empty( $alt ) ) {
        $alt = (string) get_post_meta( $image_id, '_wp_attachment_image_alt', true );
    }
    if ( empty( $alt ) ) {
        $alt = (string) get_the_title( $image_id );
    }

    return sprintf(
        '<img src="%s" alt="%s" srcset="%s" sizes="%s" width="%s" height="%s" />',
        esc_url( $img[0] ), esc_attr( $alt ),
        esc_attr( $srcset ), esc_attr( $sizes ),
        esc_attr( $img[1] ?? '' ), esc_attr( $img[2] ?? '' )
    );
}

Step 5: The full production component. All parameters in one signature — image ID, size, custom ACF alt field, fallback image URL, loading strategy, and extra CSS classes. The function adds loading="lazy" (with an eager override for above-the-fold images), decoding="async" for off-main-thread decode, a fallback image URL when no attachment exists, and full escaping on every HTML attribute. Every image in your theme is now one function call.

inc/theme-image.php
php
// ACF image field renderer
<?php
/**
 * ACF Image Field Renderer with Alt Text Fallback
 *
 * Place in inc/theme-image.php and require from functions.php.
 * One call renders a responsive <img> with 4-level alt fallback,
 * loading attribute, decoding="async", fallback image, srcset,
 * sizes, and full escaping on every attribute.
 *
 * @param int    $image_id  Attachment ID from an ACF Image field.
 * @param string $size      Registered image size name.
 * @param array  $args {
 *     @type string $acf_alt_field  ACF Text field on attachment for custom alt.
 *     @type string $fallback_url   Fallback image URL if no attachment exists.
 *     @type string $loading        lazy | eager (default: 'lazy').
 *     @type string $class          Additional CSS classes.
 * }
 * @return string Safe HTML <img> tag or empty string.
 */
function theme_render_image( int $image_id, string $size = 'large', array $args = [] ): string {
    $args = wp_parse_args( $args, [
        'acf_alt_field' => '', 'fallback_url' => '',
        'loading'       => 'lazy', 'class' => '',
    ] );
    $img = wp_get_attachment_image_src( $image_id, $size );
    if ( ! $img && ! $args['fallback_url'] ) return '';
    if ( ! $img ) $img = [ $args['fallback_url'], 0, 0 ];

    $alt = '';
    if ( $args['acf_alt_field'] ) {
        $alt = (string) get_field( $args['acf_alt_field'], $image_id ); // ACF field type: Text
    }
    if ( empty( $alt ) ) {
        $alt = (string) get_post_meta( $image_id, '_wp_attachment_image_alt', true );
    }
    if ( empty( $alt ) ) {
        $alt = (string) get_the_title( $image_id );
    }

    $loading = in_array( $args['loading'], ['lazy','eager'], true ) ? $args['loading'] : 'lazy';
    $srcset  = wp_get_attachment_image_srcset( $image_id, $size );
    $sizes   = wp_get_attachment_image_sizes( $image_id, $size );
    $class   = $args['class'] ? ' class="' . esc_attr( $args['class'] ) . '"' : '';

    return sprintf(
        '<img src="%s" alt="%s" loading="%s" decoding="async"%s srcset="%s" sizes="%s" width="%s" height="%s" />',
        esc_url( $img[0] ), esc_attr( $alt ), $loading, $class,
        esc_attr( $srcset ), esc_attr( $sizes ),
        esc_attr( $img[1] ?? '' ), esc_attr( $img[2] ?? '' )
    );
}

// ── Usage ────────────────────────────────────────
// echo theme_render_image( get_sub_field( 'hero_image' ), 'large', [
//     'acf_alt_field' => 'hero_image_alt',
//     'loading'       => 'eager',
//     'class'         => 'hero__image',
// ] );

Where to put the code

Save Step 5 in inc/theme-image.php and require it from functions.php:

require_once get_template_directory() . '/inc/theme-image.php';

Then replace every raw <img> tag that reads from an ACF Image field with echo theme_render_image( get_sub_field( 'field_name' ), 'size', $args ). The image ID source stays the same — you’re only replacing the inline HTML with a function call.

02 — How it works

How ACF image field renderer Works — Why Each Layer Exists

Here is how this ACF image field renderer works step by step: Five layers, each solving a real friction point from the previous step.

Five layers, each solving a real friction point from the previous step.

1

Raw get_sub_field() — repetition, no srcset, inconsistent alt

Raw get_sub_field() with a manual <img> tag works. The problem is not correctness — it’s discoverability and consistency. An ACF Image field returning an Image ID requires three separate function calls (get_sub_field(), wp_get_attachment_image_src(), get_post_meta()) before you can render a tag. Every flexible layout repeats the same pattern. Forget one esc_url() and you have an XSS vector. Add a new registered image size and you need to update every wp_get_attachment_image_src() call individually. Change your lazy loading strategy and you’re find-and-replacing across twenty template files.

2

theme_render_image() — one consistent call site, full escaping

theme_render_image() solves the consistency problem. One function accepts an image ID + size and returns a complete, escaped <img> tag. The output runs through esc_url() on the src and esc_attr() on every other attribute — three escaping calls total. If the attachment doesn’t exist (deleted from the Media Library after the field was saved), wp_get_attachment_image_src() returns false and the function returns an empty string — no broken image icon, no console error. Width and height from wp_get_attachment_image_src() are included so the browser reserves the correct aspect ratio before the image loads, eliminating Cumulative Layout Shift.

3

Four-level alt chain — ACF alt > WP alt > title > empty string

The four-level alt fallback chain is the main value-add of this component. Each level is checked in priority order. Level 1 — ACF custom alt field: If the developer passes an acf_alt_field name (e.g. 'hero_image_alt'), the function calls get_field( $acf_alt_field, $image_id ) to read a hand-crafted alt text stored on the attachment. This is the most intentional source — the editor wrote it specifically for this image in this context — so it wins. Level 2 — WordPress attachment alt: If no custom ACF alt field was passed, or it returned empty, the function falls back to get_post_meta(..., '_wp_attachment_image_alt', true ) — the standard alt text field every image gets in the Media Library. Level 3 — Attachment post title: If the Media Library alt field is also empty (common — many editors skip it), the function uses get_the_title( $image_id ). The attachment title is typically the original filename or a descriptive label, making it a reasonable last-resort alt text. Level 4 — Empty string: If all three sources are empty, the alt attribute is "". This is valid HTML (the attribute exists, its value is empty) and signals to screen readers that the image is decorative. It is never null — passing null to esc_attr() triggers a deprecation warning in PHP 8.1+.

4

srcset + sizes — responsive images from registered sizes

srcset and sizes make the image responsive without any manual breakpoint configuration. wp_get_attachment_image_srcset() looks at every registered image size in WordPress (core sizes plus any added via add_image_size()) and finds the closest match to each of the standard srcset width descriptors (300w, 600w, 900w, etc.). For a 2000px wide uploaded image registered at large (1024px), the browser might receive: srcset="img-300x200.jpg 300w, img-600x400.jpg 600w, img-1024x683.jpg 1024w, img-1536x1024.jpg 1536w, img-2000x1333.jpg 2000w". wp_get_attachment_image_sizes() outputs a default sizes string — typically (max-width: {size}px) 100vw, {size}px — which tells the browser: “At viewports narrower than the image’s intrinsic width, this image fills 100% of the viewport width. At wider viewports, it’s capped at its intrinsic width.” The browser then picks the smallest srcset variant that covers the calculated slot width. You can override the sizes string per layout by passing a custom one.

5

Production component — loading, decoding, fallback, class

The production component combines all five layers into a single call. wp_parse_args() merges the caller’s options with defaults so optional arguments don’t need to be passed every time. $loading is validated against a whitelist (lazy | eager) — invalid strings fall back to 'lazy' so a typo doesn’t prevent all images from loading. decoding="async" tells the browser to decode the image off the main thread, improving paint timing — this is safe on all modern browsers and is a set-and-forget attribute. The $fallback_url parameter provides an image placeholder when the attachment has been deleted — useful during development when content and media are out of sync.

03 — Setup & integration

ACF image field renderer — Setup & Integration

To integrate this ACF image field renderer into your project: The component is one PHP file. Wire it into your theme, configure your ACF Image fields, and replace your raw <img> markup.

The component is a single PHP file. Wire it into your theme, configure your ACF Image fields, and replace your raw <img> markup.

wp-content/themes/your-theme/
│   ├── inc/
│   │   └── theme-image.php
│   ├── template-parts/
│   │   └── flexible/
│   │   │   ├── hero.php
│   │   │   ├── card-grid.php
│   │       └── gallery.php
│   ├── acf-json/
│   │   ├── group_hero.json
│   │   └── group_card_grid.json
    └── functions.php
1

Create inc/theme-image.php and require it

Create inc/theme-image.php with the Step 5 code. In functions.php, add the require statement at the top of the file — before any template includes — so the helper is available everywhere:

require_once get_template_directory() . '/inc/theme-image.php';
2

Configure ACF Image fields to return Image ID

Set up your ACF Image fields. In each field group, add an Image field with Return Format → Image ID — this is the format theme_render_image() expects. If you want a custom alt text per image, add a second Text field to the same field group with a descriptive name like hero_image_alt. Example field group:

Field 1: hero_image   → Type: Image  → Return Format: Image IDnField 2: hero_image_alt → Type: Text  → Label: "Alt text override"

The second field is optional — if you skip it, the function falls through to WordPress attachment alt and post title.

3

Replace raw <img> markup with theme_render_image()

Replace raw <img> tags in your flexible content templates. In template-parts/flexible/hero.php, change:

// Before — 6 lines of manual HTML per image.n$img_id = get_sub_field( 'hero_image' );nif ( $img_id ):n    $img = wp_get_attachment_image_src( $img_id, 'large' );n    ?>n    <img src="<?php echo esc_url( $img[0] ); ?>"n         alt="<?php echo esc_attr( get_post_meta( $img_id, '_wp_attachment_image_alt', true ) ); ?>" />n    <?phpnendif;nn// After — 1 line.necho theme_render_image( (int) get_sub_field( 'hero_image' ), 'large', [n    'acf_alt_field' => 'hero_image_alt',n    'loading'       => 'eager',n    'class'         => 'hero__image',n] );

The field name in get_sub_field() stays the same. You’re only changing how the result is rendered.

4

Migrate incrementally — no downtime

If your theme already has get_sub_field() image calls in many flexible layouts, replace them incrementally. Start with the layouts you touch most — hero, card grids, featured images. Test each one by viewing the page and checking that the <img> tag in the source includes srcset, sizes, loading="lazy", and a non-empty alt attribute. Leave the rest for when you edit those templates. There is no migration cost — old get_sub_field() + manual <img> calls continue to work alongside theme_render_image().

04 — Making it your own

Making This ACF image field renderer Your Own

Customise this ACF image field renderer to match your specific needs: The component is a pattern, not a closed set. Add custom sizes attributes, picture elements for art direction, LQIP placeholders, or extract the alt logic into a standalone helper.

1

Add a custom sizes attribute for grid layouts

Add a custom sizes override for specific layouts. The default sizes string works for full-width heroes but not for card grids where images sit at 33% width. Add a sizes argument that replaces the auto-generated one:

$args['sizes'] = $args['sizes'] ?? '';nn// ... in the sprintf, use the custom sizes if provided:n$sizes_attr = $args['sizes'] ?: wp_get_attachment_image_sizes( $image_id, $size );nn// Usage for a 3-column card grid:necho theme_render_image( $image_id, 'card', [n    'sizes' => '(min-width: 768px) 33vw, 100vw',n] );

This tells the browser: “At 768px+, this card image fills 33% of the viewport. Below that, it fills 100%.” The browser then picks the smallest srcset variant that covers the calculated pixel width.

2

Add a <picture> wrapper for mobile/desktop crop variants

Add a <picture> wrapper for art direction. If your hero image is cropped differently on mobile (tall on phone, wide on desktop), you need a <picture> element with <source> media queries — not just a srcset:

function theme_render_picture( int $image_id, array $args = [] ): string {n    $args = wp_parse_args( $args, [n        'mobile_size'  => 'hero-mobile',n        'desktop_size' => 'hero-desktop',n        'breakpoint'   => '768px',n    ] );nn    $mobile  = wp_get_attachment_image_src( $image_id, $args['mobile_size'] );n    $desktop = wp_get_attachment_image_src( $image_id, $args['desktop_size'] );n    if ( ! $mobile || ! $desktop ) return '';nn    $alt = theme_image_alt( $image_id, $args['acf_alt_field'] ?? '' );nn    return sprintf(n        '<picture><source srcset="%s" media="(max-width: %s)" /><img src="%s" alt="%s" loading="lazy" decoding="async" /></picture>',n        esc_url( $mobile[0] ), esc_attr( $args['breakpoint'] ),n        esc_url( $desktop[0] ), esc_attr( $alt )n    );n}
3

Add a low-quality image placeholder (LQIP) system

Add a low-quality image placeholder (LQIP) system for perceived performance. Generate a tiny blurred version of each uploaded image and show it while the full image loads:

// Register a micro thumbnail size.nadd_image_size( 'lqip', 20, 20, true );nn// In theme_render_image(), add:nif ( $args['lqip'] ) {n    $lqip_src = wp_get_attachment_image_url( $image_id, 'lqip' );n    if ( $lqip_src ) {n        $attr .= ' style="background-image: url(' . esc_url( $lqip_src ) . '); background-size: cover;"';n    }n}

The 20px thumbnail is typically under 500 bytes — it loads instantly and gives the user a sense of content before the full-resolution image finishes downloading.

4

Extract alt logic into theme_image_alt() for reuse

Extract the alt text logic into a separate theme_image_alt() helper. If you need alt text outside of the render function — e.g. for an ARIA label or an Open Graph meta tag — the alt chain is duplicated:

function theme_image_alt( int $image_id, string $acf_alt_field = '' ): string {n    $alt = '';n    if ( $acf_alt_field ) {n        $alt = (string) get_field( $acf_alt_field, $image_id ); // ACF field type: Textn    }n    if ( empty( $alt ) ) {n        $alt = (string) get_post_meta( $image_id, '_wp_attachment_image_alt', true );n    }n    if ( empty( $alt ) ) {n        $alt = (string) get_the_title( $image_id );n    }n    return $alt;n}nn// Then theme_render_image() calls theme_image_alt() instead of inline logic.n// Usage anywhere:n$og_image_alt = theme_image_alt( $hero_id, 'hero_image_alt' );

3 things that will silently break:

1. ACF Image Return Format mismatch. theme_render_image() expects the Image field to return an Image ID (integer). If your field is set to Return Format → Image URL, wp_get_attachment_image_src() receives a URL string instead of an integer and returns false. Every image will render the fallback or an empty string. Fix: in ACF, set Return Format → Image ID on every Image field.

2. Passing a string where an integer is expected. wp_get_attachment_image_src() accepts an integer attachment ID. If you pass an ACF Image array ([ 'ID' => 42, 'url' => '...' ]) instead of just the ID, the function treats the array as int(1) and returns the wrong image. Always cast get_sub_field() to (int) or use $field['ID'] if your Return Format is set to Image Array.

3. Missing registered image sizes. wp_get_attachment_image_srcset() depends on registered image sizes existing. If you request size 'hero' but never registered it with add_image_size( 'hero', 1440, 600, true ), WordPress falls back to the full-size image and the srcset only includes that one variant — no responsive behaviour.

Performance notes:

  • ACF caches internally: Every get_sub_field() and get_field() call reads from ACF’s in-memory cache after the first access for that post during the current request. Rendering the same image field three times in a flexible layout loop makes one database query, not three.
  • Srcset generation is cached by WordPress: wp_get_attachment_image_srcset() and wp_get_attachment_image_sizes() use WordPress’s internal wp_cache_* functions. After the first call for a given attachment ID + size combination, subsequent calls on the same page hit the object cache (in-memory by default, persistent if you use Redis).
  • loading=”lazy” defers off-screen images: Browsers with native lazy loading (Chrome 77+, Firefox 75+, Safari 15.4+) only download images within a calculated distance from the viewport. Use loading="eager" for the first image in the hero — it’s guaranteed to be visible on page load and lazy-loading it would delay Largest Contentful Paint.
  • decoding=”async” improves paint timing: The browser decodes the image data off the main thread, so text and layout can render while the image is still being processed. This is a browser hint, not a guarantee — but it costs zero bytes and has no downside.
0 comments

No comments yet. Be the first.

Leave a Reply

Table of Contents

ACF image field renderer — Code Snippet

ACF image field renderer — code snippet by Mosharaf Hossain

This ACF image field renderer is a production-tested PHP snippet for WordPress developers. See the ACF Image field documentation for related official documentation.

The ACF image field renderer snippet provides a safe, accessible way to output images from ACF Image fields — handling null values, missing alt text, and return format differences in one reusable helper function. It prevents blank image tags and empty alt attributes from reaching the DOM without adding conditional clutter to every template that uses an image field.

The problem with outputting ACF image fields directly is that the return format changes the shape of the value. Image Array returns an associative array with url, alt, width, and height keys. Image ID returns an integer. Image URL returns a string. If the field is empty or the attachment was deleted, the value is null, false, or an incomplete array. Outputting the URL directly without null-checking produces a broken image tag in the HTML that fails Core Web Vitals audits and accessibility checks.

The snippet handles all three return formats. When the return value is an array, it reads the url and alt keys. When it is an integer, it calls wp_get_attachment_image_url and get_post_meta to retrieve the URL and alt text. The alt text fallback chain checks the ACF field alt, then the attachment library alt, then the image title, and finally an empty string — ensuring a valid alt attribute is always output for accessibility compliance and WCAG conformance.

The function accepts a second argument for the image size name, so you can call the renderer from any template and get the correct registered size. If the image is decorative — a background or icon with no semantic meaning — pass an empty string as the alt override to suppress the alt text intentionally. This is the correct WCAG usage for decorative images and prevents screen readers from announcing decorative content.

Place the function in inc/acf-helpers.php and require from functions.php. Set the ACF Image field’s return_format to Image Array for maximum data access, or Image ID if you need to pass the ID to WordPress image functions for srcset generation.

Tested on WordPress 6.0+ with ACF 6.0+ and PHP 8.0+. Browse all ACF snippets at Code Snippets or see the companion ACF Options Page Helper snippet. This renderer is used across every template in the Vitamines project.

Related: ACF Button Link Component — and browse the full code snippet library.

Chat on WhatsApp