How to set a Page as the parent of a WordPress Custom Post Type

Custom Post Types in WordPress 3.x are designed to work from the root of your website i.e. a flat hierarchy.

What if you needed to set a particular page or post in your website as the parent to your new custom post types?

Here’s a solution for that.

Custom Post Types

In a recent project we created a “Case Study” custom post type and added several new case study posts to our WordPress site.

We also created a page template called “Case Studies” that listed the new custom post types.

Everything was rumbling along just super until we noticed, when viewing our new custom post types, the breadcrumb trail and URL permalink weren’t including the Case Studies page that we had clicked through from.

For example.

Our Case Studies page URL was /case-studies/ and the breadcrumb trail was also /case-studies/

When clicking on a case study custom post type “Land Owners”, both the URL and breadcrumb trail changed to “/land-owners/”.

Where had the Case Studies page gone in the hierarchy?

Let me be clear, this is not a WordPress bug but rather the way custom post types are currently implemented.  And yes, it is very annoying.

WordPress assumes that all custom post types are at the top of the hierarchy. i.e. they have no associated parent.

Unfortunately, this is exactly what we weren’t looking for.

The Solution

Of course WordPress may change the way custom post types work in future versions but here’s a fix for now.

We’re going to create a meta box for the custom post type that allows us to enter the ID of a page or post and associate that with the post_parent variable, effectively making that ID our parent page.

We’re using the custom post type called “casestudy” here so change that value to your own custom post type name and copy the following into your functions.php file.

//Add the meta box callback function
function admin_init(){
add_meta_box("case_study_parent_id", "Case Study Parent ID", "set_case_study_parent_id", "casestudy", "normal", "low");
add_action("admin_init", "admin_init");

//Meta box for setting the parent ID
function set_case_study_parent_id() {
  global $post;
  $custom = get_post_custom($post->ID);
  $parent_id = $custom['parent_id'][0];
  <p>Please specify the ID of the page or post to be a parent to this Case Study.</p>
  <p>Leave blank for no heirarchy.  Case studies will appear from the server root with no assocaited parent page or post.</p>
  <input type="text" id="parent_id" name="parent_id" value="<?php echo $post->post_parent; ?>" />
  // create a custom nonce for submit verification later
  echo '<input type="hidden" name="parent_id_noncename" value="' . wp_create_nonce(__FILE__) . '" />';

// Save the meta data
function save_case_study_parent_id($post_id) {
  global $post;

  // make sure data came from our meta box
  if (!wp_verify_nonce($_POST['parent_id_noncename'],__FILE__)) return $post_id;
    if(isset($_POST['parent_id']) && ($_POST['post_type'] == "casestudy")) {
      $data = $_POST['parent_id'];
      update_post_meta($post_id, 'parent_id', $data);
add_action("save_post", "save_case_study_parent_id");

There are three functions here.

  1. function admin_init() – This initialises the meta box for our casestudy custom post type, pointing to the call-back function that creates it
  2. function set_case_study_parent_id() – This is the call-back function that contains the HTML code needed to create the meta box on the casestudy custom post type when in edit mode
  3. function save_case_study_parent_id() – This saves the meta data from the new box into the DB

This will give you the following new meta box on your custom post type, allowing you to enter the ID of a page or post which it will set as the parent document.

We set the meta box value to the ID of our “Case Studies” page which displayed all the custom post types.

Great, now our Yoast breadcrumb trail was picking up the Case Studies parent page from our custom post type, but what about the URL?

There was just one thing left to do to bring everything together.

No slug had been explicitly defined for the custom post type, so they were being displayed in the URL from the server root with just the post title i.e. “/land-owners/”.

We needed to define a slug that would match up with our Case Studies page (slug “case-study”) so that our URL would read “/case-studies/land-owners/”.

Going back into the function that registered our custom post type, we added the following line to the $args array:

'rewrite' => array('slug' => 'case-studies', 'with_front' => true)

This rewrites the URL slug from the server root to include “case-studies” in front of the post title.

Just for reference, here’s the complete custom post type registration function with the above line included.

/* Custom Post Types */
function register_custom_post_case_study() {

$labels = array(
'name' => _x('Case Studies', 'post type general name'),
'singular_name' => _x('Case Study', 'post type singular name'),
'add_new' => _x('Add New', 'Case Study'),
'add_new_item' => __('Add New Case Study'),
'edit_item' => __('Edit Case Study'),
'new_item' => __('New Case Study'),
'view_item' => __('View Case Study'),
'search_items' => __('Search Case Studies'),
'not_found' =>  __('No Case Studies found'),
'not_found_in_trash' => __('No Case Studies found in Trash'),
'parent_item_colon' => ''

$args = array(
'labels' => $labels,
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'query_var' => true,
'rewrite' => array('slug' => 'case-studies', 'with_front' => true),
'capability_type' => 'post',
'hierarchical' => false,
'menu_position' => null,
'supports' => array('title','editor','author','excerpt','page-attributes')

register_post_type( 'casestudy' , $args );
add_action('init', 'register_custom_post_case_study');

Now our URL matches the breadcrumbs and the Case Studies pages is the parent of our custom post type.

You may have to go into your Permalinks settings and, without making any updates, click the Save Changes button to update the new URL structure.

If you found this useful and are living near Dublin, Ireland, why not catch up with us at our regular monthly Dublin WordPress meetup.

Dublin WordPress

Attention all WordPress Users in Dublin

If you live in and around Dublin and use WordPress as a blogger, developer, designer, trainer, business venture etc, come and join us on the last Thursday of every month at the Bull & Castle (next to Christ Church) from 6.30pm onwards.

More details at

Post maintained by .

  • Mitch010


    I think this is something very close to my problem. I do not have many knowledge (yet) of WordPress and PHP, but I’m learning. My problem is that, like you, everything seems to be working alright, but when I’m on a product page the breadcrumb and category are not linking to the page I want them to link to. Example (in Dutch sorry): If you look at the breadcrumb link for ‘Benalmadena’ you see that it is linking to /hotels-in/benalmadena. This is the permalink I had to set up in the WP settings, but I’d rather use the page /hotels/benalmadena which is created by Woocommerce (I think?). Now, it works both ways which creates duplicate content. Can you tell me if you have the solution for this? I tried your code, but nothing is showing up in the Product settings like you are explaining. You’d be my hero!

  • Gravitational FX Web Design

    Sorry Mitch but the links you posted do not seem to work.

  • SB

    Thanks for writing this up. Extremely straightforward.

    The only issue is I am trying to do almost the exact opposite. I have a custom post type of locations. Then a series of pages that make up supporting content like prices and upcoming activities. Is there a way to reverse engineer this so a page can identify a custom post as its parent?

    I came across this which seemed like it would relieve the restrictions of heirarchy between differnt content types.

    But it is over a year old so probably out of date.

    I would appreciate any of your thoughts as this has me tearing my hair out.


  • gusadit


    Thanks for the post. It’s really helpful.

    But, I have a question. How do I remove the ‘casestudy’ slug from the URL? For example:


  • limecanvaswil

    Thanks @gusadit:disqus

    Line 24.

  • gusadit

    Just remove it or change it?

  • limecanvaswil

    On line 24 rename the “case-studies” to the slug that you want to appear or remove if you don’t want any.