Custom Post Types in WordPress

Register a Custom Post Type

Use the WP function register_post_type() with the init action hook in the functions.php theme file to define a custom Post Type.

NOTE: It is necessary to simply save the Settings > Permalinks page to make the new post type register.
NOTE: The custom post type archive page will be in a (slug friendly) URL path named after the post-type. In the example below, a new "Visual Art" posts will be at mysite.com/visual-art/my-post/ and the archive page will be at mysite.com/visual-art/.

// `Visual Art` custom post type function.
function create_art_posttype() {

    register_post_type( 'visual_art',
        // CPT Options
        [
            'labels' => [
                'name'                => __('Visual Art', 'wph-text-domain'),
                'singular_name'       => __('Visual Art', 'wph-text-domain'),
                'menu_name'           => __('Visual Art', 'wph-text-domain'),
                'parent_item_colon'   => __('Parent Visual Art', 'wph-text-domain'),
                'all_items'           => __('All Visual Art', 'wph-text-domain'),
                'view_item'           => __('View Visual Art', 'wph-text-domain'),
                'add_new_item'        => __('Add New Visual Art', 'wph-text-domain'),
                'add_new'             => __('Add New Visual Art', 'wph-text-domain'),
                'edit_item'           => __('Edit Visual Art', 'wph-text-domain'),
                'update_item'         => __('Update Visual Art', 'wph-text-domain'),
                'search_items'        => __('Search Visual Art', 'wph-text-domain'),
                'not_found'           => __('Visual Art Not Found', 'wph-text-domain'),
                'not_found_in_trash'  => __('Visual Art Not found in Trash', 'wph-text-domain'),
            ],
            'public'                  => true,
            'has_archive'             => true,
            'rewrite'                 => ['slug' => 'visual-art'],
            'show_in_rest'            => true,
            'capability_type'         => 'post',

            // Taxonomies.
            'taxonomies'              => ['post_tag'],

            // Features this custom post type supports in Post Editor
            'supports'                => ['title', 'editor', 'excerpt', 'author', 'thumbnail', 'comments', 'revisions', 'custom-fields',],

            // A hierarchical custom post type is like Pages and can have
            // Parent and child items. A non-hierarchical custom post type
            // is like Posts.
            'hierarchical'            => false,
            'show_ui'                 => true,
            'show_in_menu'            => true,
            'show_in_nav_menus'       => true,
            'show_in_admin_bar'       => true,
            'menu_position'           => 5,
            'can_export'              => true,
            'exclude_from_search'     => false,
            'publicly_queryable'      => true,

        ]
    );
}
// Hooking up our function to theme setup
add_action( 'init', 'create_art_posttype' );

Custom Post Type Thumbnail Support

Thumbnail support must be specifically added to a custom post type.

// Add the `visual_art` custom post type thumbnails to the theme.
function add_visual_art_theme_supports() {

    // Add thumbnail support to the `art` type template files.
    add_theme_support('post-thumbnails', ['visual_art']);
}
add_action('after_setup_theme', 'add_visual_art_theme_supports');

Custom Post Type Conditionals

Custom post types will respect the normal is_single() and is_archive() functions but you can also check for specific post types with the is_singular() and get_post_type() functions.

// Single custom post type page.
if( is_singular( 'visual_art' ) )
{
    do something.
}

// Check within The Loop.
if( 'visual_art' == get_post_type() )
{
    do something.
}

Custom Post Type Template Files

The default template files for a custom post type are single-{post type ID}.php and archive-{post type ID}.php. So for the Visual Art post type they would be:

single-visual_art.php
archive-visual_art.php

Add a Custom Post Type to the Main Query

// Add custom post-types to the 'Main Query' (Home page).
add_action( 'pre_get_posts', 'add_my_post_types_to_query' );
function add_my_post_types_to_query( $query )
{
    if ( is_home() && $query->is_main_query() )
    {
        $query->set( 'post_type', array(
            'post',         // Keep the standard 'posts' in the Main Query.
            'visual_art ', // Add the custom post-type 'visual_art ' to the Main Query.
            //'page',       // Add pages to the Main Query.
        ) );
    }
    return $query;
}