It is no secret that I use ACF in almost all themes I develop. Why? Because it’s way faster and easier to add meta fields to post types. On top of that, creating Gutenberg blocks has never been easier with the help of acf_register_block_type().

One of the cons of bypassing the native way of handling meta fields is that we are using ACF API throughout our code.

Core functions like get_field() rely on the ACF plugin being active all the time. And it makes sense to be that way since we don’t have those functions available in the WordPress core.

But what happens if someone “accidentally” deactivates the plugin? Or an update doesn’t go as planned and the plugin stops working? Well, nothing good for sure – a lot of warnings, missing content, and even fatal errors.

Is there a way to avoid it? Yes!

I’ve been integrating the ACF plugin into all my non-commercial themes for a while. That gives me a lot more control and I ship everything into one repository. Plus, I am sure that everything works with this specific version of ACF.

How to do it?

First, I like to keep all my external plugins and libraries that I integrate under /inc/plugins within the theme directory. That makes it easy to understand and update in the future ( you simply replace the files ).

So our ACF folder goes here as well. The new full path to the plugin then becomes /inc/plugins/acf ( presuming the folder name is acf ).

Register the plugin core file and change the paths

Our next step is to actually register the ACF plugin through our theme. For this purpose, I like to have a separate file called register-plugins.php inside my /inc folder, which is then included in the functions.php.

Again, that gives a lot more clarity and a nicer theme structure.

The full code that we need is the following:

<?php
/**
 * Load MU plugins
 *
 * Loads the integrated plugins/libraries into the theme code.
 * No need of class, use anonymous functions
 *
 * @since      1.0.0
 * @subpackage neshable
 * @author     Nesho Sabakov 
 */


// Include the ACF plugin.
include_once( get_template_directory() . '/inc/plugins/acf/acf.php' );

// Customize ACF path
add_filter('acf/settings/path', function( $path ) {
	// update path
	$path = get_template_directory() . '/inc/plugins/acf/';
	// return
	return $path;
});

// Customize ACF dir
add_filter('acf/settings/dir', function( $dir ) {
	// update path
	$dir = get_template_directory_uri() . '/inc/plugins/acf/';
	// return
	return $dir;
});

Going through the lines, we first include our main plugin file – acf.php. We then use acf/settings filter to tell the plugin its new location inside our theme folder.

At this point, we have a fully working ACF plugin integrated into our theme.

Add support for child themes

When integrating the ACF plugin, we need to use additional filters if you plan on using a child theme.

Use the following filters to modify the acf-json folder path when a child theme is active. That way you will load the parent theme custom fields json files, and any new field groups will be saved into the child theme acf-json folder.

In this case, the child theme contains its own custom fields which don’t interfere with the parent theme’s custom fields.

Paste the following filters into the file:

// Add support for child themes - load child acf and parent
add_filter('acf/settings/load_json', 'my_acf_json_load_point');
function my_acf_json_load_point( $paths = array() ) {
	$paths = array( get_template_directory() . '/acf-json' );

	if ( is_child_theme() ) {
		$paths[] = get_stylesheet_directory() . '/acf-json';
	}

	return $paths;
}

// Add support for child themes - save to child acf-json as well
add_filter('acf/settings/save_json', 'my_acf_json_save_point');
function my_acf_json_save_point( $path = '' ) {
	$path = get_template_directory() . '/acf-json';

	if ( is_child_theme() ) {
		$path = get_stylesheet_directory() . '/acf-json';
	}

	return $path;
}

Optional: Hide the ACF menu on the live site

A cool little trick here is to hide the entire ACF menu completely. By doing this you can prevent the client from accidentally deleting or modifying a field group on his own.

Use the following condition to achieve this:

if ( wp_get_environment_type() == 'production' ) {
	// (Optional) Hide the ACF admin menu item.
	add_filter( 'acf/settings/show_admin', 'my_acf_settings_show_admin' );

	function my_acf_settings_show_admin( $show_admin ) {
		return false;
	}
}

Remember that this condition relies on wp_get_environment_type() which defaults to ‘production‘. Therefore you need to set the WP_ENVIRONMENT_TYPE global variable to local, development, or staging in order to see the menu.