Practical WordPress Development

Adding a Custom Post Type To An Existing Menu

Up until this point, I’ve never worked on a project or done any type of work that required a custom post type to be added to an existing menu in the WordPress dashboard.

For the most part, I’m generally of the mindset that custom post types should:

  • Exist as top level menus
  • Should be added at the bottom of the WordPress dashboard menu

This mentality is primarily motivated by the fact that I see the core WordPress menu options as first-class citizens in the dashboard, custom post types as being second-class citizens.

That’s just a rule of thumb, though. There are always exceptions.

But there are also times where custom post types could be treated as, say, third-class citizens where they should be integrated with an existing menu be it a core menu or another custom post type menu.

Luckily, it’s trivially easy to add a custom post type as a menu item to an existing menu.

Adding The Custom Post Types

To demonstrate this, we’ll add two custom post types: Portfolios and Locations.


The portfolio is meant to represent a collection of galleries, so this would ideally create a ‘Portfolio’ menu where the user would be able to add a new, edit, search, and manage galleries:

// Define the 'Portfolio' post type. This is used to represent galleries
// of photos. This will be our top-level custom post type menu.
$args = array(
  'labels'	=>	array(
            'all_items'           => 'Gallery',
						'menu_name'	      		=>	'Portfolio',
						'singular_name'       =>	'Gallery',
					 	'edit_item'           =>	'Edit Gallery',
					 	'new_item'            =>	'New Gallery',
					 	'view_item'           =>	'View Gallery',
					 	'items_archive'       =>	'Gallery Archive',
					 	'search_items'        =>	'Search Portfolio',
					 	'not_found'	      		=>	'No galleries found',
					 	'not_found_in_trash'  =>	'No galleries found in trash'
	'supports'			=>	array( 'title', 'editor', 'author', 'revisions' ),
	'menu_position'	=>	5,
	'public'				=>	true
register_post_type( 'portfolio', $args );

This will introduce the Portfolio menu into the WordPress dashboard menu. If you’ve been working with custom post types for a while, there’s nothing really new in this specific code.


Next, we’ll add the menu for the Locations. It’s roughly the same as the code above except we aren’t including a menu_postion.

Instead, we’re adding a parameter for the show_in_menu option:

// Next, we'll define a second custom post type called 'Locations' where we could
// potentially display a list of locations that are used as part of our portfolio.
// This custom post type will be added as a submenu to the 'Portfolio' menu
$args = array(
  'labels'	=>	array(
						'all_items'           => 	'Locations',
						'menu_name'	          =>	'Locations',
						'singular_name'       =>	'Location',
					 	'edit_item'           =>	'Edit Location',
					 	'new_item'            =>	'New Location',
					 	'view_item'           =>	'View Location',
					 	'items_archive'       =>	'Location Archive',
					 	'search_items'        =>	'Search Locations',
					 	'not_found'	          =>	'No locations found.',
					 	'not_found_in_trash'  => 'No locations found in trash.'
	'supports'      =>	array( 'title', 'editor', 'revisions' ),
	'show_in_menu'  =>	'edit.php?post_type=portfolio',
	'public'		    =>	true
register_post_type( 'location', $args );

Notice that the show_in_menu parameter accepts a URL of the page to which this menu should be added.

edit.php?post_type=portfolio is obviously the URL for our Portfolio custom post type; however, you could potentially specify the URL to, say, simply edit.php to add the menu to the Posts menu.

Get The Gist

Anyway, easy enough, right?

I’ve created a gist of this entire code that can be viewed here on GitHub. Feel free to add comments or modifications as needed as this is primarily for demonstration purposes.

It obviously would need to be registered with the proper hooks and actions to be integrated into WordPress.


  1. Carrie

    Nice! Like you, I’ve only ever needed to add CPTs as top-level, but this is a cool bit of code to know about.

    As far as dashboard citizenry goes…. Depending on the CPT and it’s prominence on the site, it may get a higher place in the menu. For instance, I did a recruiting site earlier this year with a Jobs CPT. It’s the primary menu item the client will be using/ looking for on the menu, so I positioned it pretty close to top. Not necessarily right, just my approach. :)


    • Tom McFarlin

      For instance, I did a recruiting site earlier this year with a Jobs CPT. It’s the primary menu item the client will be using/ looking for on the menu, so I positioned it pretty close to top. Not necessarily right, just my approach.

      Totally! If you’re using WordPress as a CMS for jobs and positions, then Jobs are definitely higher rated than anything else.

      Great example :).

  2. curtismchale

    I’ve used them as children of existing items when we’ve been working with post to post relationships. So with a Company as the parent CPT and Teams as the child CPT (which divided company users in to – Teams).

    That’s the only example I have though. Any other time that might have been used, we ended up building a custom UI and just hide the WordPress admin UI for them.

    • Tom McFarlin

      As far as hiding the WordPress admin UI is concerned, how far have you taken that?

      I ask because I’ve done some things as small as just removing menu items and dashboard widgets to writing full on custom views that don’t allow anyone except an administrator to see the actual dashboard.

      I’m always curious how other developers do it.

  3. Butch

    Tom, is there a way to add custom post types to a custom menu item?

    I’m working on a e-commerce theme that sells different types of products that one post type is just not able to handle. Is it possible to add a custom menu item named something like “Products” and have two or three custom post types under that menu?

    • Tom McFarlin

      There is a way to do it, but you won’t necessary have the same functionality as a top-level menu.

      For example, say that you have a custom post type called Portfolio (like in the example above), and you normally have the two menu items:

      View All
      All New

      You can add another custom post type, say, Images, to the Portfolio menu by passing this as an argument to the $args array:

      'show_in_menu' => 'edit.php?post_type=portfolio'

      Where you pass the ID of the post type defined as the first argument of register_post_type. This will introduce an “Images” menu item so the list now looks like:

      View All
      All New

      Does this make sense, or did I misunderstand your question?

      • Butch

        More like how the Settings menu is. Where the top level menu is not a post type but the items bellow it are different settings.

        Products (not a post type)
        -Product 1 (custom post type)
        -Product 2 (custom post type)
        -Product 3 (custom post type)

        • Tom McFarlin

          Oh, gotcha! Yeah – for something like that you’d want to check out add_menu_page and add_submenu_page.

          Those functions do exactly what you’re describing – it takes a little more work because you’ll need to setup options and what not, but this should take care of what you need.

          • Butch

            Thanks, I’ll give those a look.

            • wunderdojo

              Your question is old but figured this might come in handy for someone else. I just spent a long time looking through the WP source code trying to do the same thing: I have a custom menu created using add_menu_page(). It has a handful of sub_menu items. I have a custom post type that is related to this menu and I wanted the add / list links for that post type to be submenu items.

              Short answer: No easy way to do it in WP. The way it generates the menus and custom post type UI means there is no simple call you can make.

              1. Register your post type with the following options:
              ‘show_ui’ => true,
              ‘show_in_menu’ => false
              In this example my custom post type is acct_notes

              2. In your theme’s functions.php file or your plugin file, add an action to admin_menu:
              add_action(‘admin_menu’, ‘modify_admin_menus’);

              3. Add submenu_pages for the two post UI links — list posts, create new post.
              The first param is the slug of the menu you’re adding it to. The second is the name that will show up on the page. The third is what the link will show up as. The fourth is the capability required to access it. The fifth is the slug for this menu item. And last is the function that will generate the output. In this case we’re not going to use it so we can pass NULL.

              add_submenu_page(‘members’, ‘Account Notes’, ‘Account Notes’, ‘manage-options’, ‘view_account_notes’, NULL);

              4. The last step is the function that will change those submenu links so that they go to the correct post UI pages.

              function modify_admin_menus(){
              global $submenu;
              if(array_key_exists(‘members’, $submenu)){
              foreach($submenu[‘members’] as $key => $value){
              $k = array_search(‘view_account_notes’, $value);
              $submenu[‘members’][$key][$k] = (current_user_can($submenu[‘members’][$key][1]))? admin_url(‘/edit.php?post_type=acct_notes’):”;

              $l = array_search(‘new_account_note’, $value);
              $submenu[‘members’][$key][$l] = (current_user_can($submenu[‘members’][$key][1]))? admin_url(‘/post-new.php?post_type=dojo_acct_notes’) : ”;}

              What’s going on in the function is that it’s checking the global $submenu array to see if our top level menu item (members) exists — it might not if the user doesn’t have permission to access it. If they do it then looks to see if that top level menu item has a submenu page with the slug “view_account_notes” — what we assigned in add submenu_page. Finally, it checks the permission we assigned in add_submenu_page and if the current user has that permission it outputs the new link. If they don’t it removes the link altogether.

              We do that same check twice — once for the link to list our custom post type, and once for the link to add a new custom post type.

              It would be far preferable to have this capability built in, but this will let you add custom post type user interfaces to any menu you want.

              • Gregor


                I stumbled upon this post as I also wanted to include a custom post type under a custom menu page. After reading your workaround I thought that there must be an easier way for this, so I poked around the code and I found the solution:

                when registering a custom post type set the ‘show_in_menu’ argument to false,
                when adding a sub-menu item set the callback function to NULL and the menu_slug to the custom post type, e.g. ‘edit.php?post_type=your_custom_post_type_name_goes_here’

                That’s it :) for more info you can check out my post (i hope links are allowed and that I don’t break any rules) :

                • Tom

                  You haven’t broken any rules ;). Thanks for the comment and sharing the link, Gregor!

                • wunderdojo

                  Nice! Thanks for sharing the tip.

  4. Juan Ruiz

    Thanks. It works as expected

  5. MyPampanga

    What’s up, its pleasant paragraph regarding media print, we all understand media is a impressive source
    of facts.

  6. Jesin

    Tom is it possible to add a post type archive link to the menu without using plugins?

    • Tom McFarlin

      If you’re talking about adding a link to the menu in the admin dashboard, you don’t necessary have to use a plugin, but you will have to add the code into functions.php at the very least.

  7. Nathan

    Hey Tom,

    Is it possible to use this method and still retain the “add_new” link/label? When I add the custom post type to the menu of another CPT I only get the “all_items” link/label.

    • Tom McFarlin

      Honestly, I’d have to dig more into attempting that particular use case myself.

      I think that it can be done, I just haven’t personally had the need to do that. At least not yet ;).

  8. Musaddiq Khan

    I m having the problem to show the custom post type in Appearance -> Menu.
    I’ve set the three attributes
    'show_ui' => TRUE, 'show_in_menu' => TRUE, 'show_in_nav_menus' => TRUE
    and then in screen option I checked the custom post type but in vain.

    • Tom

      I’m not sure if I’m understanding your question correctly, but try copying the code in this gist into your functions.php file and make the adjustments as necessary for your installation.

      If it works, then there’s likely a problem with the code you’ve manually written; otherwise, something may have changed in core.

      Let me know how it turns out and I may need to provide an update on this post.

    • Musaddiq Khan

      I post it wrongly, actually I tried to check the custom post type in screen option but I can’t find it there.

  9. Iago Melanias

    Is anyway to show the custom post type menu inside of the ‘posts’ menu?
    Thank you.

    • Tom

      Maybe – I haven’t tested this but you can experiment with this argument:

      'show_in_menu' => 'edit.php?post_type=portfolio'

      And rather than using edit.php?post_type=portfolio you may be able to do just edit.php. Additionally, you may be able to sniff out some other default options that are set somewhere in WordPress based on the vanilla custom post type that’s being displayed.

  10. Hardeep Chauhan

    Thanks, It was really helpful, what i was looking for.

Leave a Reply

© 2020 Tom McFarlin

Theme by Anders NorenUp ↑