Does WordPress support an unlimited or large number of custom post types?

Does WordPress support an unlimited or large number of custom post types?

Yes-ish, but that would lead to a WP Admin area that’s impractically large. Also, keep in mind it would make the REST API discovery slower due to the larger number of items, as well as making templating more cumbersome due to the larger number of templates.

Keep in mind as well that because post types are registered on every page load, and stored in memory, that if you registered hundreds of post types that would mean a slowdown on every possible type of request WP could handle.

I want to build a directory site where the posts would be post types and the directory items would be standard post archive entries.

Aha, I would strongly advise against this, it’s a bad idea, and will cause issues for your with complexity.

You would be better off with a single CPT, and using a custom taxonomy to organise them. Custom post types shouldn’t be used to group things, that’s what a taxonomy is for. Taxonomies can provide the URLs, archives, templates etc that you’d need for a directory.

As an additional note, it’s much easier to list terms in a taxonomy, then to list all the registered post types. Just call get_terms, there are even some helper functions that will give you a formatted list of terms. Terms can have subterms too, so you can add additional hierarchy. Posts can have terms in multiple taxonomies too!


Edit: I decided to run some experiments. Behold! A truly awful plugin:

<?php
/**
 * Plugin Name: 200 post types
 * Description: An awful plugin to prove a point
 */

add_action( 'init', 'tomjn_add_post_types' );
function tomjn_add_post_types() {
    for ($i=0; $i < 200; $i++) {

        $labels = array(
            'name'               => _x( 'CPT '.$i, 'post type general name', 'your-plugin-textdomain' ),
            'singular_name'      => _x( 'CPT'.$i, 'post type singular name', 'your-plugin-textdomain' ),
            'menu_name'          => _x( 'CPTs '.$i, 'admin menu', 'your-plugin-textdomain' ),
            'name_admin_bar'     => _x( 'CPT'.$i, 'add new on admin bar', 'your-plugin-textdomain' ),
            'add_new'            => _x( 'Add New', 'cpt'.$i, 'your-plugin-textdomain' ),
            'add_new_item'       => __( 'Add New CPT'.$i, 'your-plugin-textdomain' ),
            'new_item'           => __( 'New CPT'.$i, 'your-plugin-textdomain' ),
            'edit_item'          => __( 'Edit CPT'.$i, 'your-plugin-textdomain' ),
            'view_item'          => __( 'View CPT'.$i, 'your-plugin-textdomain' ),
            'all_items'          => __( 'All CPTs'.$i, 'your-plugin-textdomain' ),
            'search_items'       => __( 'Search CPTs'.$i, 'your-plugin-textdomain' ),
            'parent_item_colon'  => __( 'Parent CPTs:'.$i, 'your-plugin-textdomain' ),
            'not_found'          => __( 'No CPTs found.', 'your-plugin-textdomain' ),
            'not_found_in_trash' => __( 'No CPTs found in Trash.', 'your-plugin-textdomain' )
        );

        $args = array(
            'labels'             => $labels,
            'public'             => true,
            'publicly_queryable' => true,
            'show_ui'            => true,
            'show_in_menu'       => true,
            'query_var'          => true,
            'capability_type'    => 'post',
            'has_archive'        => true,
            'hierarchical'       => false,
            'supports'           => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' )
        );

        register_post_type( 'cpt'.$i, $args );
    }
}

This allowed me to modify the number in the for loop to register progressively higher numbers of post types. There’s a very real cost to registering post types.

Here is a truncated screenshot of 200:

enter image description here

I had firefox take a fullpage screenshot, and tweeted it, but Twitters image service could not handle the dimensions.

This is on a fast machine with the default theme, and only query monitor activated for timing measurements, on WP 5.2.4.

Of note, the average speed was ~0.1s/0.2s, and with 200 CPTs it rose to 0.4/0.6s. Also note that the admin side menu is completely unusable, and the menu order system has broken down. You will need to account for this, and specify a value when registering the post type.

Clearly this isn’t enough, I decided to push it further.

Around 400 CPTs the side menu broke and overflows, at this point the average load time was ~1.6-1.7s, meaning at least a second and a half was added by CPT registration alone.

I bumped it up to 2000 CPTs:

enter image description here

Notice that pages now take on average 10 seconds to load. Memory consumption has also skyrocketed to 30MB

I bumped the number up to 20k, and the site failed to load. I reduced this to 10k, then 5k, and finally the site came back at 4k CPTs

In order to get the permalinks settings page to load, I had to go down to 2k CPTs. At this point the page took 13 seconds to load, and the resulting admin page was 12.7MB to transfer, most of which was side menu markup

Leave a Comment