How to Create a Custom Post Type in WordPress

WordPress has standard post types like posts and pages, but the code below creates a new custom post type called Portfolio. The code saves the data you entered in the meta boxes (e.g., client name, project URL) whenever you save a portfolio item. Displays the portfolio items on the website’s frontend using a shortcode.

// Register Custom Post Type "Portfolio"
function create_portfolio_cpt() {
    $labels = array(
        'name'                  => 'Portfolios',
        'singular_name'         => 'Portfolio',
        'menu_name'             => 'Portfolios',
        'name_admin_bar'        => 'Portfolio',
        'add_new'               => 'Add New',
        'add_new_item'          => 'Add New Portfolio',
        'new_item'              => 'New Portfolio',
        'edit_item'             => 'Edit Portfolio',
        'view_item'             => 'View Portfolio',
        'all_items'             => 'All Portfolios',
        'search_items'          => 'Search Portfolios',
        'not_found'             => 'No portfolios found.',
    );

    $args = array(
        'labels'             => $labels,
        'public'             => true,
        'show_in_menu'       => true,
        'supports'           => array('title', 'editor', 'thumbnail', 'custom-fields'),
        'has_archive'        => true,
    );

    register_post_type('portfolio', $args);
}
add_action('init', 'create_portfolio_cpt');

// Add Meta Boxes for Portfolio
function portfolio_meta_boxes() {
    add_meta_box(
        'portfolio_details',
        'Portfolio Details',
        'portfolio_meta_box_callback',
        'portfolio'
    );
}
add_action('add_meta_boxes', 'portfolio_meta_boxes');

// Meta Box Callback Function
function portfolio_meta_box_callback($post) {
    $client_name = get_post_meta($post->ID, 'client_name', true);
    $project_url = get_post_meta($post->ID, 'project_url', true);
    $project_date = get_post_meta($post->ID, 'project_date', true);
    $project_description = get_post_meta($post->ID, 'project_description', true);
    ?>
    <p>
        <label for="client_name">Client Name:</label>
        <input type="text" name="client_name" id="client_name" value="<?php echo esc_attr($client_name); ?>" />
    </p>
    <p>
        <label for="project_url">Project URL:</label>
        <input type="url" name="project_url" id="project_url" value="<?php echo esc_attr($project_url); ?>" />
    </p>
    <p>
        <label for="project_date">Project Date:</label>
        <input type="date" name="project_date" id="project_date" value="<?php echo esc_attr($project_date); ?>" />
    </p>
    <p>
        <label for="project_description">Short Description:</label><br>
        <textarea name="project_description" id="project_description" rows="4" style="width: 100%;"><?php echo esc_textarea($project_description); ?></textarea>
    </p>
    <?php
}

// Save Meta Box Data
function save_portfolio_meta_box_data($post_id) {
    if (array_key_exists('client_name', $_POST)) {
        update_post_meta($post_id, 'client_name', sanitize_text_field($_POST['client_name']));
    }
    if (array_key_exists('project_url', $_POST)) {
        update_post_meta($post_id, 'project_url', esc_url($_POST['project_url']));
    }
    if (array_key_exists('project_date', $_POST)) {
        update_post_meta($post_id, 'project_date', sanitize_text_field($_POST['project_date']));
    }
    if (array_key_exists('project_description', $_POST)) {
        update_post_meta($post_id, 'project_description', sanitize_textarea_field($_POST['project_description']));
    }
}
add_action('save_post', 'save_portfolio_meta_box_data');

// Display Portfolio on Frontend
function display_portfolio_items() {
    $args = array('post_type' => 'portfolio', 'posts_per_page' => 10);
    $loop = new WP_Query($args);

    while ($loop->have_posts()) : $loop->the_post(); ?>
        <h2><?php the_title(); ?></h2>
        <p><?php the_content(); ?></p>
        <p><strong>Client:</strong> <?php echo esc_html(get_post_meta(get_the_ID(), 'client_name', true)); ?></p>
        <p><strong>Project URL:</strong> <a href="<?php echo esc_url(get_post_meta(get_the_ID(), 'project_url', true)); ?>" target="_blank"><?php echo esc_url(get_post_meta(get_the_ID(), 'project_url', true)); ?></a></p>
        <p><strong>Project Date:</strong> <?php echo esc_html(get_post_meta(get_the_ID(), 'project_date', true)); ?></p>
        <p><strong>Description:</strong> <?php echo esc_html(get_post_meta(get_the_ID(), 'project_description', true)); ?></p>
    <?php endwhile;
    wp_reset_postdata();
}

// Add shortcode for frontend display
add_shortcode('display_portfolio', 'display_portfolio_items');