WPGEARED2.0.
WordPress Development

Render an ACF Link Field Safely in a WordPress Template

Published Updated WPGeared
Share Report:

Advanced Custom Fields can return a Link field as either a URL string or an array containing url, title, and target. Template code must match the return format selected in the field settings. Mixing the two formats is the most common reason a link prints incorrectly or triggers PHP warnings.

The ACF Link field documentation shows both return formats. For reusable buttons and editorial links, the array format is usually more useful because editors can control the destination, visible text, and new-window preference in one field.

Recommended field configuration

  1. Create or edit the ACF Link field.
  2. Set Return Value to Link Array.
  3. Use clear field instructions so editors know what the link does.
  4. Require the field only when the template cannot work without it.

A field name such as profile_link is easier to maintain than a generic name such as url, especially when a component has several links.

Safe template pattern

<?php
$link = get_field( 'profile_link' );

if ( is_array( $link ) && ! empty( $link['url'] ) ) {
    $url    = $link['url'];
    $title  = ! empty( $link['title'] ) ? $link['title'] : 'Visit profile';
    $target = '_blank' === ( $link['target'] ?? '' ) ? '_blank' : '_self';
    $rel    = '_blank' === $target ? 'noopener noreferrer' : '';
    ?>
    <a
        class="profile-link"
        href="<?php echo esc_url( $url ); ?>"
        target="<?php echo esc_attr( $target ); ?>"
        <?php if ( $rel ) : ?>rel="<?php echo esc_attr( $rel ); ?>"<?php endif; ?>
    >
        <?php echo esc_html( $title ); ?>
    </a>
    <?php
}
?>

This pattern handles four responsibilities explicitly:

  • is_array() confirms that the field uses the expected return format.
  • esc_url() prepares the destination for an HTML URL attribute.
  • esc_attr() prepares attribute values.
  • esc_html() prepares the human-readable link text.

ACF's get_field() reference states that values returned through the template API should be escaped for their output context. Escaping when saving is not a substitute for escaping when rendering.

Why the target value is normalized

The template does not print an arbitrary target. It permits _blank only when the field explicitly requests it and falls back to _self otherwise. When a link opens a new tab, rel="noopener noreferrer" prevents the opened page from receiving an unsafe reference to the original window.

Do not force every external link into a new tab. The target should follow the interaction requirement, and the visible text should make the destination understandable without relying on surrounding layout.

If the Link field returns only a URL

When the field's return format is Link URL, treat the value as a string and provide link text in the template:

<?php
$url = get_field( 'profile_link' );

if ( is_string( $url ) && '' !== $url ) :
    ?>
    <a href="<?php echo esc_url( $url ); ?>">Visit the team profile</a>
    <?php
endif;
?>

Do not index into $url['title'] in this configuration. It is a string, not an array.

Common failures

  • The URL appears as the visible text. The template is printing the URL instead of the array's title value.
  • PHP reports an array-to-string conversion. The field returns an array, but the template tries to echo the whole value.
  • The link disappears when the title is blank. Add meaningful fallback text or validate the editor input.
  • The link works but is unsafe. Check contextual escaping and new-tab rel values.
  • The field works on one page only. Confirm the post ID or options-page context passed to get_field().

Test the component, not only the PHP

Verify an empty field, an internal URL, an external URL, a new-tab link, a long title, and keyboard focus. Inspect the rendered HTML and test the link in the actual card, directory row, or button component where it will be used. That closes the gap between a syntactically valid snippet and a maintainable interface.