Adding custom settings to fields

Overview

Each ACF field type may contain several different settings that can be used to customize the appearance and functionality of the field. There are two main types of these settings: global and type-specific.

Global field settings are registered once and show up for every field type. An example of a global setting would be the “Field Name” setting – this shows up for every ACF field as it is necessary for the fields to function.

Type-specific settings are registered for each field type and can be used for more granular control for a specific field. For example, the Image field has type-specific settings for the min/max image size, as well as the allowed file types for the image.

This doc will explain how you can add custom field settings to both existing field types and your own custom field types.

Changelog

Adding a Global Field Setting

As mentioned above, global field settings are displayed for every field type. In ACF 6, these settings are displayed on the “General” tab for each field.

A screenshot of the new global field setting.

The following example shows how to add a new setting called “Admin Only” which will add a true/false toggle to all fields, allowing them to be hidden from non-admin users.

1. Adding the Setting

Global settings can be added using the acf/render_field_settings action hook and the acf_render_field_setting() function. The following snippet will add a global setting to the “General” tab for all field types. Please note that the third parameter for acf_render_field_setting() is set to true to acknowledge this is a global setting.

function my_admin_only_render_field_settings( $field ) {
    acf_render_field_setting( $field, array(
        'label'        => __( 'Admin Only?', 'my-textdomain' ),
        'instructions' => '',
        'name'         => 'admin_only',
        'type'         => 'true_false',
        'ui'           => 1,
    ), true ); // If adding a setting globally, you MUST pass true as the third parameter!
}
add_action( 'acf/render_field_settings', 'my_admin_only_render_field_settings' );

2. Using the Setting

With the new “Admin Only?” setting added, we can hook into any part of ACF and use the setting. The following code will hook into the acf/prepare_field filter which is run just before a field is rendered into a form. This will allow us to check if the current user is an administrator, and if not, prevent the field from being displayed:

function my_admin_only_prepare_field( $field ) {
    // Bail early if no 'admin_only' setting or if set to false.
    if ( empty( $field['admin_only'] ) ) {
        return $field;
    }

    // Prevent field from displaying if current user is not an admin.
    if ( ! current_user_can( 'manage_options' ) ) {
        return false;
    }

    // Return the original field otherwise.
    return $field;
}
add_filter( 'acf/prepare_field', 'my_admin_only_prepare_field' );

3. Testing the Setting

With the above code added to the functions.php file and a field using the “Admin Only?” setting, we can now login with either an administrator or editor account and note the field’s existence!

Adding Settings for a Specific Field Type

Field settings can be added or restricted to individual field types (text, image, etc.). In ACF 6.0 and above, these settings can also be shown in specific tabs within a field type (General, Validation, Presentation, or Conditional Logic).

The following action hooks can be used for adding field settings to these tabs:

  • acf/render_field_settings/type={type} (legacy action for the “General” tab – can be used in ACF 5 to add field type-specific settings)
  • acf/render_field_general_settings/type={type} (for adding settings to the “General” tab)
  • acf/render_field_validation_settings/type={type} (for adding settings to the “Validation” tab)
  • acf/render_field_presentation_settings/type={type} (for adding settings to the “Presentation” tab)
  • acf/render_field_conditional_logic_settings/type={type} (for adding settings to the “Conditional Logic” tab)

These filters are also applied on the acf_field base class, so if you have a custom field type that extends acf_field, you can instead just add one or more of the following methods to your custom class:

  • render_field_settings()
  • render_field_general_settings()
  • render_field_validation_settings()
  • render_field_presentation_settings()
  • render_field_conditional_logic_settings()

The following example will add a new “Exclude Words” setting to the “Validation” tab of the textarea field type. This adds a text input which we’ll later use to store a list of words that shouldn’t be allowed in textareas.

A screenshot of the new field type specific setting.

1. Adding the Setting

Since this example is adding a setting to the “Validation” tab, we’ll be using the acf/render_field_validation_settings/type={$type} action hook to render the setting.

Please note that the third parameter for the acf_render_field_setting() function is excluded – making it specific to the field, rather than a global setting as shown above.

function my_textarea_render_field_settings( $field ) {
    acf_render_field_setting( $field, array(
        'label'        => __( 'Exclude Words', 'my-textdomain' ),
        'instructions' => __( 'Enter words separated by a comma', 'my-textdomain' ),
        'name'         => 'exclude_words',
        'type'         => 'text',
    ) );
}
add_action( 'acf/render_field_validation_settings/type=textarea', 'my_textarea_render_field_settings' );

2. Using the Setting

With the new “Exclude Words” setting added and a new textarea field added (using the setting), we can hook into any part of ACF and use the setting. The following code will hook into the acf/validate_value filter which is run during validation. This will allow us to check if the current value contains any words from the exclude list and notify the user via a validation error message.

function my_textarea_validate_value( $valid, $value, $field, $input ) {
    // Bail early if the field is already invalid.
    if ( ! $valid ) {
        return $valid;
    }

    // Bail early if the "Exclude Words" setting is not set.
    if ( empty( $field['exclude_words'] ) ) {
        return $valid;
    }

    // Explode the words into an array we can loop over.
    $words = explode( ',', $field['exclude_words'] );
    $words = array_map( 'trim', $words ); // Trim white spaces.
    $words = array_values( $words ); // Remove empty values.

    foreach( $words as $word ) {
        // Check if the word exists.
        if( stripos( $value, $word ) !== false ) {
            $valid = sprintf( __( 'Value must not include "%s"', 'my-textdomain' ), $word );
            break;
        }
    }

    return $valid;
}
add_filter( 'acf/validate_value/type=textarea', 'my_textarea_validate_value', 10, 4 );

3. Testing the Setting

With the above code added to the functions.php file and a field using the ‘exclude_words’ setting, we can now edit a post and attempt to save a value.

A screenshot of the custom error message added above.

Upgrading Existing Field Settings to ACF 6

Custom field settings that were added using the acf/render_field_settings or acf/render_field_settings/type={type} action hooks will continue to work in ACF 6, with the settings appearing on the “General” tab for any fields those settings were added to.

However, there are some changes in ACF 6 that may require some additional code.

Moving an Existing Field Setting into a Different Tab

If you want to move your custom field setting to a different tab and also support users who may still be on ACF 5, you’ll likely need to add some code to make sure that the setting is registered differently on ACF 5 and 6 to prevent the setting from showing up twice.

One way to do that would be to update your existing field setting code to return early if on ACF 6.0 or above.

function render_field_settings( $field ) {
    // We’re on ACF 6.0 or above, so we don’t need to register the setting here.
    if ( version_compare( ACF_MAJOR_VERSION, '6.0', '>=' ) ) {
        return;
    }

    // Rest of field settings code.
}

You could then add a separate function that hooks on one of the tab-specific action hooks as mentioned above.

Changes to acf_render_field_setting() in ACF 6

In ACF 5, field settings were rendered as table rows inside a parent table with a class of .acf-table. In ACF 6 and above, field type settings are no longer rendered inside of a table.

As a result, the acf_render_field_setting() function has been updated to render the field settings created by that function inside of <div> elements by default.

This likely won’t cause any issues if you’re using acf_render_field_setting() in one of the action hooks mentioned above or in a class that extends acf_field.

While not recommended, it is possible to use the acf_render_field_setting() function outside of those action hooks. In that case, we would recommend checking any settings created with that function to ensure that they are displayed as expected, and updating your code as necessary.

Changes to Field Setting Instructions in ACF 6

In ACF 6, the value of the instructions parameter passed to acf_render_field_settings() is now rendered as a tooltip to reduce UI clutter.

A tooltip in ACF 6.

If you would rather display the instructions inline with the field setting (such as in ACF 5), you can do so by changing the instructions parameter to hint:

function my_textarea_render_field_settings( $field ) {
    acf_render_field_setting( $field, array(
        'label' => __( 'Exclude Words', 'my-textdomain' ),
        'hint'  => __( 'Enter words separated by a comma', 'my-textdomain' ),
        'name'  => 'exclude_words',
        'type'  => 'text',
    ) );
}
add_action( 'acf/render_field_validation_settings/type=textarea', 'my_textarea_render_field_settings' );

This will render the provided hint text underneath the field setting:

An ACF field setting hint.

Conclusion

Adding extra field settings in ACF is easy and the possibilities are endless!

Related