How to Run Shortcodes in ACF Text and Textarea Fields

Quick Summary

  • Add add_filter( 'acf/format_value/type=textarea', 'do_shortcode' ) to functions.php, or type=text for single-line fields, and shortcodes in those fields run when you output them.
  • Use name= or key= to enable shortcodes for one field only, and leave them off fields that Subscribers or visitors fill in: whoever edits the field can run any shortcode.
  • Since ACF 6.2.7, the_field() strips forms, iframes and scripts from the output; allow them for one trusted field with the acf/the_field/allow_unsafe_html filter.

Advanced Custom Fields (ACF) returns Text and Textarea values exactly as they were typed, so a shortcode such as [gallery] in one of those fields shows up as plain text on your page. To make it run, add an acf/format_value filter that passes the field value through WordPress’s do_shortcode() before ACF returns it. The WYSIWYG Editor field already runs shortcodes and needs no filter.

Add the filter to your child theme’s functions.php file or to a small plugin. The first line covers every Textarea field on the site, the second every single-line Text field; keep the one you need, or both.

PHP
add_filter( 'acf/format_value/type=textarea', 'do_shortcode' );
add_filter( 'acf/format_value/type=text', 'do_shortcode' );

Then output the field as usual with get_field() or the_field(). To test it, register a small shortcode such as the current year shortcode, type [year] into a field and load the page.

How the filter works

  • ACF runs acf/format_value whenever it formats a value for a template, for example in get_field(), the_field() and get_sub_field().
  • Besides the general hook, ACF fires three narrower versions of it: acf/format_value/type=… for one field type, name=… for one field name and key=… for one field key. The acf/format_value documentation lists them.
  • do_shortcode() finds every registered shortcode in the value and replaces it with that shortcode’s output. add_filter() passes only the first argument unless you ask for more, so do_shortcode() receives just the field value.

The filter only changes formatted values. If you load a field with formatting turned off, by passing false as the third argument of get_field(), you get the raw text with the shortcode in it.

How do you enable shortcodes for one field only?

Use the name= version of the hook with the field name, or key= with the field key (it starts with field_). That’s the better choice when only one or two fields should run shortcodes, for example a footer note:

PHP
function mytheme_acf_field_shortcodes( $value ) {
    if ( ! is_string( $value ) ) {
        return $value;
    }

    return do_shortcode( shortcode_unautop( $value ) );
}
add_filter( 'acf/format_value/name=footer_note', 'mytheme_acf_field_shortcodes' );
  • Inside a Repeater or Group, name= matches the sub field’s own name, such as note, not the stored name like rows_0_note.
  • ACF always fires the name= and key= hooks after the type= hook, and it applies a Textarea’s New Lines setting on the type= hook. With “Automatically add paragraphs”, the shortcode is already wrapped in <p> tags by the time this function runs, so a shortcode that returns a <div> would end up inside a paragraph. shortcode_unautop() removes the paragraph tags around a shortcode that stands on its own line, the same step WordPress runs on post content before do_shortcode().
  • The is_string() check returns anything that isn’t text unchanged.

The one-line type=textarea filter doesn’t need shortcode_unautop(): ACF adds its own Textarea formatting on the init hook, after functions.php has loaded, so a filter added in functions.php runs first and the new-line formatting comes after the shortcode output.

Why does the_field() remove part of the shortcode’s output?

Since ACF 6.2.7, the_field() and the_sub_field() escape what they print with wp_kses() and the HTML allowed in post content. Tags outside that list are removed, so a shortcode that outputs a form, input fields, an <iframe> or a script loses them. A form shortcode printed through the_field() keeps only its <button>, for example. get_field() doesn’t escape anything. ACF explains the change in its HTML escaping guide.

If only trusted users edit the field, allow the full output for that one field with the filter ACF provides for this:

PHP
add_filter( 'acf/the_field/allow_unsafe_html', function ( $allowed, $selector ) {
    if ( 'signup_form' === $selector ) {
        return true;
    }

    return $allowed;
}, 10, 2 );

$selector is the field name you pass to the_field(). echo get_field( 'signup_form' ); prints the same unescaped output without a filter.

Who can run shortcodes through the field?

do_shortcode() runs every registered shortcode it finds, no matter who typed it. The filter therefore lets everyone who can edit the field run any shortcode on your site:

  • Fields on posts and pages: users who can edit that post. They can already use shortcodes in the post content, so little changes.
  • Options pages: an options page added with acf_add_options_page() requires the edit_posts capability by default, which Contributors have. Set a stricter capability for the options page if that’s too wide.
  • User profiles and front-end forms: a field group shown on the user profile screen can be filled in by Subscribers on their own profile, and an acf_form() on the front end by whoever can see the form.

A type=textarea filter covers all of these fields at once. When a site has fields that untrusted users fill in, enable shortcodes by name= or key= on the fields your editors use instead.

Common problems

  • The shortcode still shows as text. Check that the field type matches the filter (type=textarea or type=text) and that you output the field with formatting on. get_post_meta() reads the raw value and skips ACF’s formatting altogether.
  • One shortcode works, another doesn’t. do_shortcode() only replaces registered shortcodes, so the plugin or theme that adds the shortcode must be active.
  • The shortcode’s HTML appears as code. If your template escapes the value with esc_html(), the markup the shortcode returns is escaped too. wp_kses_post() keeps the HTML allowed in post content; escaping output with esc_html and friends explains the difference.

Frequently asked questions

Do shortcodes work in ACF WYSIWYG fields?

Yes. The WYSIWYG Editor field formats its value like post content: it runs its own acf_the_content filter, which includes wpautop() and do_shortcode(). It needs no extra filter.

Does this work in Repeater and Group sub fields?

Yes. A type= filter also catches sub fields of that type, and a name= filter matches the sub field’s own name.

Is this the same as the [acf] shortcode?

No, it’s the opposite direction. [acf field="…"] shows a field’s value inside post content. Since ACF 6.3 that shortcode is disabled by default on new installs; the ACF shortcode documentation shows how to turn it back on with the enable_shortcode setting.

If your theme needs fields like these set up so editors can update content without breaking the layout, see my custom WordPress development services.

Share on X (opens in a new tab) Share on LinkedIn (opens in a new tab) Share on Facebook (opens in a new tab)

ChatGPT (opens in a new tab) Claude (opens in a new tab) Gemini (opens in a new tab) Perplexity (opens in a new tab) Grok (opens in a new tab)

More Insights

See More