WordPress Custom Post Type Function Without A Plugin

Updated November 18th, 2018
Updated November 18th, 2018
Share this post:
Share on facebook
Share on twitter
Share on reddit
Share on linkedin
Share on pinterest
Share on pocket
Share on email
Share on print

This is the code you need to create a bare-bones WordPress custom post type that will allow you to better organize the posts on your site. This code was originally posted on the WordPress Codex.

Basic Implementation

function create_posttype() {
  register_post_type( 'wpll_product',
      'labels' => array(
        'name' => __( 'Products' ),
        'singular_name' => __( 'Product' )
      'public' => true,
      'has_archive' => true,
      'rewrite' => array('slug' => 'products'),
add_action( 'init', 'create_posttype' );

Advanced Implementation

The code block below registers the widgets custom post type, customizes the labels and adds a lot of the WordPress editor capabilities to the editor for that post type.

function create_advanced_posttype() {
 * Register a widgets post type.
 * @link http://codex.wordpress.org/Function_Reference/register_post_type
// Define the labels used inside the WordPress admin for this custom post type (CPT)
    $labels = array(
        'name'                => _x( 'Widgets', 'Post Type General Name', 'textdomain' ),
        'singular_name'       => _x( 'Widgets', 'Post Type Singular Name', 'textdomain' ),
        'add_new'             => __( 'Add New', 'textdomain' ),
        'add_new_item'        => __( 'Add New Widget', 'textdomain' ),
        'edit_item'           => __( 'Edit Widget', 'textdomain' ),
        'new_item'            => __( 'New Widget' ),
        'all_items'           => __( 'All Widgets', 'textdomain' ),
        'view_item'           => __( 'View Widget', 'textdomain' ),
        'search_items'        => __( 'Search Widget', 'textdomain' ),
        'menu_name'           => __( 'Widgets', 'textdomain' ),
        'parent_item_colon'   => __( 'Parent Widget', 'textdomain' ),
        'update_item'         => __( 'Update Widget', 'textdomain' ),
        'not_found'           => __( 'Not Found', 'textdomain' ),
        'not_found_in_trash'  => __( 'Not found in Trash', 'textdomain' )
// Define more options for the CPT
    $args = array(
        'label'               => $labels,
        'description'         => __( 'Widget information and details', 'textdomain' ),
        'labels'              => $labels,
        // Define the allowed features in the post editor
        'supports'            => array( 'title', 'editor', 'author', 'excerpt', 'thumbnail', 'comments', 'trackbacks', 'post-formats', 'page-attributes', 'custom-fields', 'revisions', ),
        // If hierarchical is true then the CPT behaves like a post. If false, it can have parent and child items like a page. 
        'hierarchical'        => false,
        'public'              => true,
        'show_ui'             => true,
        'show_in_menu'        => true,
        'show_in_nav_menus'   => true,
        'show_in_admin_bar'   => true,
        // menu_position defines where in the WP admin menu the CPT appears. 5 puts it right below posts. The higher the number to lower the CPT will be.
        'menu_position'       => 19,
        'can_export'          => true,
        'has_archive'         => true,
        'exclude_from_search' => false,
        'publicly_queryable'  => true,
        'capability_type'     => 'post',
    // Register this CPT
    register_post_type( 'widgets', $args );
// hook into the init action and call create_advanced_posttype when it fires
add_action( 'init', 'create_advanced_posttype', 0 );

The code block below adds two taxonomies to the Widgets custom post type: type and creator. The Type taxonomy behaves like categories because the “hierarchical” argument is set to true.

The Creator taxonomy is basically the same, but it behaves like tags because the “hierarchical” argument is set to false.

function create_widget_taxonomies() {
 * Register a widgets post type.
 * @link https://codex.wordpress.org/Function_Reference/register_taxonomy
	// create two taxonomies, type and creators for the post type "widget"
	// Add new taxonomy, hierarchical (this makes it like categories)
	$labels = array(
		'name'              => _x( 'Widget Types', 'taxonomy general name', 'textdomain' ),
		'singular_name'     => _x( 'Widget Type', 'taxonomy singular name', 'textdomain' ),
		'search_items'      => __( 'Search Widget Types', 'textdomain' ),
		'all_items'         => __( 'All Widget Types', 'textdomain' ),
		'parent_item'       => __( 'Parent Widget Type', 'textdomain' ),
		'parent_item_colon' => __( 'Parent Widget Type:', 'textdomain' ),
		'edit_item'         => __( 'Edit Widget Type', 'textdomain' ),
		'update_item'       => __( 'Update Widget Type', 'textdomain' ),
		'add_new_item'      => __( 'Add New Widget Type', 'textdomain' ),
		'new_item_name'     => __( 'New Widget Type Name', 'textdomain' ),
		'menu_name'         => __( 'Type', 'textdomain' ),

	$args = array(
		'hierarchical'      => true,
		'labels'            => $labels,
		'show_ui'           => true,
		'show_admin_column' => true,
		'query_var'         => true,
		'rewrite'           => array( 'slug' => 'widget-type' ),

	register_taxonomy( 'widget-type', array( 'widgets' ), $args );

	// Add new taxonomy, NOT hierarchical (this makes it like tags)
	$labels = array(
		'name'                       => _x( 'Widget Creators', 'taxonomy general name', 'textdomain' ),
		'singular_name'              => _x( 'Widget Creator', 'taxonomy singular name', 'textdomain' ),
		'search_items'               => __( 'Search Widget Creators', 'textdomain' ),
		'popular_items'              => __( 'Popular Widget Creators', 'textdomain' ),
		'all_items'                  => __( 'All Widget Creators', 'textdomain' ),
		'parent_item'                => null,
		'parent_item_colon'          => null,
		'edit_item'                  => __( 'Edit Widget Creator', 'textdomain' ),
		'update_item'                => __( 'Update Widget Creator', 'textdomain' ),
		'add_new_item'               => __( 'Add New Widget Creator', 'textdomain' ),
		'new_item_name'              => __( 'New Widget Creator Name', 'textdomain' ),
		'separate_items_with_commas' => __( 'Separate widget creators with commas', 'textdomain' ),
		'add_or_remove_items'        => __( 'Add or remove widget creators', 'textdomain' ),
		'choose_from_most_used'      => __( 'Choose from the most used widget creators', 'textdomain' ),
		'not_found'                  => __( 'No widget creators found.', 'textdomain' ),
		'menu_name'                  => __( 'Creators', 'textdomain' ),

	$args = array(
		'hierarchical'          => false,
		'labels'                => $labels,
		'show_ui'               => true,
		'show_admin_column'     => true,
		'update_count_callback' => '_update_post_term_count',
		'query_var'             => true,
		'rewrite'               => array( 'slug' => 'widget-creator' ),

	register_taxonomy( 'widget-creator', 'widgets', $args );

// hook into the init action and call create_book_taxonomies when it fires
add_action( 'init', 'create_widget_taxonomies', 0 );

With the following code block we create a meta box called “Widget Details” and set it to display at the top of the right sidebar in the post editor.

Then we add “Price” and “Currency” fields to the metabox.

Then we allow the input field content to be saved to the database as post meta data.

// create the meta box for widget price
function widget_details_box() {
        __( 'Widget Details', 'textdomain' ), // title of the meta box in the editor
        'widgets', // post type to add the metabox to
        'side', // context (placement) of the metabox in the editor: side or 
        'high' // position of metabox

add_action( 'add_meta_boxes', 'widget_details_box' );

// add the contents of the meta box for widget price
function widget_details_box_content( $post ) { ?>

And after all that, we have a full-featured custom post type with a functioning meta box for custom information. Next we have to display the information we've entered into the custom post type and the meta box display on a page.

To do that, paste the code below inside the WordPress loop on the page template for the custom post type.

ID, 'widget_price', true);
   $widget_currency = get_post_meta($post->ID, 'widget_currency', true);
   // check if the widget_price variable has a value
   if($widget_price){ ?>

The price of the widget is:

If you prefer to go the plugin route, Advanced Custom Fields will help you make custom meta boxes very easily.

Share this post:
Share on facebook
Share on twitter
Share on reddit
Share on linkedin
Share on pinterest
Share on pocket
Share on email
Share on print
Share on facebook
Share on twitter
Share on linkedin
Share on pinterest
Share on email


Your email address will not be published. Required fields are marked *

  1. Please sir, i want to remove email and website fields in comment form for my WordPress blog, can you help me?

    1. You should be able to to go to Settings then Discussion. Uncheck the box labelled “Comment author must fill out name and email”. Save changes.

      Then use the CSS display:none; applied to the name and email field.

      That process should make it so the name and email address field are not required and not visible.

      I hope that helps!

    1. Hi Gulshan,

      The location that custom post types appear depends on the name you give to the template file. For the branches custom post type the template file should be called single-branches.php

      If that is the case, regular blog posts should not be appearing where the single-branches.php is loading.