Display custom post type in template

Create custom page template

The first step is to create a custom page template to hold the code. For example, name the file template-cpt-list.php:

<?php
/**
 * Template Name: Custom Post Types List
 */

get_header();

// Custom code will go here

get_footer();

?>

Create list of generated Custom Post Types (CPTs)

The next step is generating your list of CPTs. There’s a core function for that: get_post_types():

<?php get_post_types( $args, $output, $operator ); ?>

So, for example, to return public, custom post types by name:

$custom_post_types = get_post_types( 
    array(
        // Set to FALSE to return only custom post types
        '_builtin' => false,
        // Set to TRUE to return only public post types
        'public' => true
    ),
    // Set to "objects", so we have the full CPT object
    'objects'
 );

Now, your CPTs are contained in a variable that has an array of CPT objects.

Create list of CPT archive index permalinks

So the next step is to take the CPT objects, and use them to create a list of permalinks to each CPT archive index. There is also a core function for that, as well: get_post_type_archive_link():

<?php get_post_type_archive_link( $posttype ); ?>

So we just step trough our array of post type names, and retrieve the permalink for each:

foreach ( $custom_post_types as $custom_post_type ) {
    $custom_post_type->permalink = get_post_type_archive_link( $custom_post_type->name );
}

Then you can use that array of permalinks (URLs) to create your list markup:

<ul>
<?php
foreach ( $custom_post_types as $custom_post_type ) {
    echo '<li>';
    echo '<a href="' . $custom_post_type->permalink . '">' . $custom_post_type->name . '</a>';
    echo '</li>';
}
?>
</ul>

Putting it all together

Your template-cpt-list.php should now look like so:

<?php
/**
 * Template Name: Custom Post Types List
 */

get_header();

// Get list of CPTs
$custom_post_types = get_post_types( 
    array(
        // Set to FALSE to return only custom post types
        '_builtin' => false,
        // Set to TRUE to return only public post types
        'public' => true
    ),
    // Set to "objects", so we have the full CPT object
    'objects'
 );

// Add CPT permalinks to CPT objects
foreach ( $custom_post_types as $custom_post_type ) {
    $custom_post_type->permalink = get_post_type_archive_link( $custom_post_type->name );
}

// Output CPT archive index permalinks list
echo '<ul>';
foreach ( $custom_post_types as $custom_post_type ) {
    echo '<li>';
    echo '<a href="' . $custom_post_type->permalink . '">' . $custom_post_type->name . '</a>';
    echo '</li>';
}
echo '</ul>';

get_footer();

?>