Adding a Sub Options Page in a Custom Post Type Menu

July 12, 2017

LLT Group

One of the greatest things about WordPress is the ease and extensibility it lends to developers, especially when it comes to expanding upon or modifying its own code.   While the possibilities of WordPress are nigh on endless, it can be a different sort of challenge to present and format its backend magic in a way that is intuitive and easy to remember for a client.  Take custom post types as an example; WordPress does a great job of integrating custom post types into the dashboard sidebar and presenting all the relevant custom field groups whenever a user creates a new instance of that post type.

But let’s say you wanted to customize the archive page for that post type, and give it its own unique hero image on the frontend when all the post types are displayed.  The solution is simple: you would make a global options page with a field for that hero image, and call that field in the template file.  By running the following code in your functions.php file:

if( function_exists(‘acf_add_options_page’) ) {

$parent = acf_add_options_page(array(

‘page_title’ => ‘Theme General Settings’,

‘menu_title’ => ‘Theme Settings’,

‘menu_slug’  => ‘theme-general-settings’,

‘redirect’   => false



you will add a new global options page named ‘Theme Settings’ to the WordPress dashboard.  This page will save any advanced custom field values assigned to it to the wp_options table, which is available globally throughout your site and not restricted to any particular page.  So in our example, if we had a custom post type of Services, and we wanted a single unique hero image for the Services archive page, we can create that field group and assign it to our Theme Settings page.  From there, whenever we want to call that field in the markup of our template files, we would run the_field(‘hero_image’, ‘options’).  The second parameter of this call specifies the post id of the field.  Since we are calling the field from its place in the Theme Settings options page, we use our Theme Settings page’s post id, which defaults to ‘options’ but can be changed to a number or custom string.

So currently, this works perfectly to display the hero on our archive; however, while the results are the same on the frontend, the backend location of the Services hero image is in Theme Settings.  This could be confusing to remember for a client, who might expect all relevant fields for a custom post type and its archive page to be grouped together as opposed to scattered under different sections of the dashboard.

Luckily, there is a very simple solution to this.  Another useful function is acf_add_options_sub_page, which allows for making a sub-page of the global options page.  Usually you’ll do this within the options page itself, for organizing your global fields (for example, a header sub-page and a footer sub-page), but for our purposes, we can modify it a touch and include it right inside the menu of our Services post type.  Run the following code just underneath where we declared our Theme Settings options page:


acf_add_options_sub_page( array(

‘page_title’ => ‘Services Options’,

‘parent’     => ‘edit.php?post_type=service’,

‘post_id’    => ‘service’,

) );

Here we are adding the sub options page in the menu for our Services post type.  We give it a page title of ‘Services Options’, which is how it will be displayed in the menu, and we also specify a key of parent, which is set to the anchor link of whatever menu item we want to select on the dashboard.  To set it to your own custom post type, just replace the value of ‘service’ with whichever post type you want it to appear under.  You can also change its post id to any number or custom string, so that in the markup you could call the global field by an ID of ‘service’ instead of the default value of ‘options’.

If we look in the dashboard, we can see the Services Options sub-page is now sitting nicely underneath ‘Add New Service’ in the Services menu.  This is perfect for a client, as it serves to group everything related to that post type together in one easy-to-remember location on the backend.  Not only does it save both the client and developer time, it also presents a logical relationship between that post type and all its pertinent global fields.

Start a Project