Creating a Custom Post Type could be a little complicated.

But there’s a few ways that you can try creating a custom post type with a little less pain. First of all, let’s have a look at what the basic requirements are:

  1. You’ll need to define the Custom Post Type somewhere that WordPress will know in order to show you the button and other links on your Dashboard toolbar.
  2. In addition, you’ll probably want some taxonomies (ie: categories) to assign to your Custom Post Type.
  3. You’ll probably want to have some custom fields in your Custom Post Type
    (other than just the simple Title, Content, Featured Image etc).
  4. You’ll want to have an archive display (a list of posts with thumbnail image, title, excerpt etc) for your Custom Post Type.
  5. Then you’ll need a display for each individual post.


We’ll look at each of these topics one by one. But to put the whole job in perspective, this tutorial has the following goals:

  1. Create a Custom Post Type. We’ll be creating Testimonials.
  2. Create PHP files for post archives and individual testimonials.
  3. In the following tutorial I’ll show you how you can create a front-end user interface where people can add a testimonial via a form with customised fields and an image upload.
    This would create the testimonial and save as a draft pending admin approval.
    (You wouldn’t want testimonials to be posted without some kind of oversight, right?)

If we were to do this from the ground up, with nothing but PHP, HTML and CSS, we could. A purist would want to tailormake each component lovingly by hand, but me, I just want to get the job done as cleanly as possible with a minimum of effort.

As I always say to my students “I’m gonna teach you to be the laziest developers alive”. After their initial disbelief, I add “Because you’ll be creating tight, reusable and modular systems that’ll allow you more time for the beach, bar or biking, whatever floats your boat”.


In order to get to market quickly, we’re going to use a bunch of tools. Some of them are free, some paid. But trust me, they work and will save you a bunch of time. They’ll pay themselves off in the first job.

  • To create the Custom Post Type, we’ll be using CPTUI (open source)
  • To create the Custom Fields, we’ll use Advanced Custom Fields (open source)
  • To create the user form, we’ll use Formidable Forms. Not necessary to complete this tutorial, but essential in the next one.(open source).

Hang on – did I say that you’d have to pay somewhere? Well actually I did – there are ‘Pro’ versions of each of these plugins, which add significant extra functionality to the base level plugins. But the only one that’s absolutely necessary for this process to work is the Formidable Forms Pro version (About $49 for a single license). I’ll be dealing with that in the follow-on tutorial.

Installing Plugins

You’ll need to go to the links I gave you in the previous section. I presumed that you know how to download the plugins and install them.
If not – just click ‘Plugins’ in the Dashboard toolbar, then ‘Add New’ at the top of the page. Do a search for the plugins I’ve specified, and follow the install prompts. It really is that simple.  With the Formidable Forms, you’ll have to part with some cash. But as I said, it’ll be worth it.

You’ll note that I’ve installed the Pro version of Advanced custom fields as well as the Extended version of CPTUI. However, this tutorial does not make use of the advanced features afforded by those versions. So don’t sweat.

  1. Download and install the plugins.
  2. Activate the plugins.
Custom Post Types plugin list

Creating the Custom Post Type

We’ll use CPTUI to create the Custom Post Type.

  1. Find the button on the Dashboard toolbar, and choose ‘Add/Edit Post Types’.
  2. Add the post slug, plural and singular labels. This is what will be displayed on the Dashboard toolbar. You can also go through the Additional labels and customise them too.
  3. In the Settings area, set ‘Has Archive’ to true. (we want to list a number of posts on the archive page).
  4. Set ‘Capability type’ to post.
  5. Set ‘Hierarchical’ to false – we’ll only have a flat non-nesting category base.
  6. Check Title and Featured image in the  ‘Supports’ section.
  7. You could also create some custom Taxonomies, but we’ll leave that for the moment.
  8. Save your Custom Post Type.

Checking the details.

If you click the link ‘Registered Types/Taxes in the Dashboard, you’ll see the makeup of your new Custom Post Type. You can edit, get the code, or view the archive if you have made one (which we haven’t yet).

Creating the Custom Fields

We’ll use Advanced Custom Fields Pro to make the special fields that we’ll use in our Custom Post Type.

  1. Click ‘Custom Fields’ in the Dashboard toolbar.
  2. Click ‘Add New’ on the top of the page. Name the Field Group ‘Testimonial Fields’. You can name it anything you like, but this seems appropriate.
  3. Click the ‘Add Field’ button to add a text field. Add a Field Label, field name (lowercase, no spaces).
  4. Set the ‘Field Type’ to Text.
  5. Set ‘Required’ to yes.
  6. You can add other info in this panel, but these are the basics.

Now create a bunch of other fields. I’ve set the following fields/types

Label : slug/field name : Type.

  • First Name : first_name : Text
  • Last Name : last_name : Text
  • Email Address : email_address : Email
  • Testimonial Type : testimonial_type : Radio Button (Add the content in the ‘Choices’ section of the field)
  • Company Name : company_name : Text
  • Job Type : job_type : Select (dropdown : Add the content in the ‘Choices’ section of the field)
  • Rating : rating : Radio Button (Add the content in the ‘Choices’ section of the field)
  • Message : message : Wysiwyg Editor
  • Image Upload : image_upload : Image

Now define which post type these fields should show up on in the editing environment. In the Location settings, create the rule:

  1. Show this field group if Post Type is equal to Testimonial (the Custom Post Type you created in the previous section)
  2. Publish/Update your field group

Updating your Permalinks

Sometimes WordPress gets confused!! OK we all do, but here’s a way not to get frustrated down the track.

In your Dashboard toolbar, click ‘Settings’ > ‘Permalinks’ . Then choose ‘default’, save the change, then turn the display to ‘Post Name’ and save again.

Creating a new Testimonial

We’ll create a new Testimonial in the way you’d create any new post. As previously stated, we haven’t discussed taxonomies yet. (categories).

  1. Click ‘Add New’ from the Dashboard toolbar > Testimonials or the button at the top of the page.
  2. Add details for your new testimonial. You’ll see all the fields you created in the previous section.
  3. Add a featured image in the usual way.
  4. Publish your new Testimonial

Now I bet you’re all excited to see the result of all this hard work. But you’ll just have to be a bit patient for a while. It’s coming, I promise. If you had the Extended version of CPTUI installed, this would be a simple shortcode pasted into any text area on a page. But we’re going to create some PHP files in the WordPress theme folder.

And cut some code. Like real code monkeys. ;)

Before you code edit: Child Themes

It is important that you make any edits to a Child Theme, rather than the Parent Theme. Did I lose ya? OK so let’s say you’re using the TwentySeventeen theme that comes along with any standard installation of WordPress. Imagine that you’ve done a whole bunch of customisation to various theme files – CSS, PHP etc. Then you get a notification that your theme has been updated and would you like to download and install the bright and shiny new version. Of course you do, so you do. And then realise that all your hard work has disappeared into thin air. That’s because the files got overwritten. Of course, you made a file and database backup first. Right?

The way to avoid this trainwreck is to use Child Themes. The easiest way to create a Child Theme is to use a plugin like Orbisius Child Theme Creator. I’m not going into that process in this tutorial, but I’d expect that you would have created a Child Theme and we’d be creating/working on those files, not the main Theme’s files.

Creating an archive page for your Testimonial Custom Post Type.

First up, this is going to require a code editor along with some precision and patience. My favourite code editor is Sublime Text (open source donationware).

We’re going to create four files. These are stored in : siteroot/wp-content/themes/child-theme-directory.  I’ve also created a  subdirectory template-parts which contains a directory called testimonial. It’s not mission critical that you create these subdirectories, but it is good practice. If in doubt, check my screenshot. (directory = folder)

Create a new PHP file and save it as archive-testimonial.php in the Child Theme’s root directory. The naming convention is important. Essentially WordPress looks for the archive filename by default. For more info on this topic, check the official WordPress Codex on template hierarchy.

Now add the following code to the file. (I’ve kept this really simple with a bare minimum of containers).

You’ll see the line right in the middle of the loop that points to a file that will contain the actual archive post – with thumbnail image, title. You could build it all in this file, but I prefer to build components that can be easily switched in and out.

get_template_part( 'template-parts/testimonial/content', get_post_format() );

 * Testimonials
 * @package Theme Name
get_header(); ?>
<div id="primary" class="content-area">
	<div class="jr-testimonial-container">
		if ( have_posts() ) : ?>
			/* Start the Loop */
			while ( have_posts() ) : the_post();
				get_template_part( 'template-parts/testimonial/content', get_post_format() );
		endif; ?>
	<!-- /jr-testimonial-container -->
</div><!-- #primary -->
<?php get_footer(); ?>

Creating the archive content.

Create a new PHP file and save it as content.php in siteroot/wp-content/themes/child-theme-directory/template-parts/testimonial

Add the following code to the file. Essentially it creates an article tag for each testimonial post that we plan on displaying on the archive page. The container gets a unique ID from the_ID(). The thumbnail image used as the featured image on your post is displayed via the_post_thumbnail().

The Title of the post is supplied via the_title() and in this case, we’re wrapping it in an anchor tag that will link to the single post which is grabbed via esc_url( get_permalink().

 * Template part for displaying testimonial archive posts
 * @package Theme Name
 * @since 1.0
 * @version 1.0

<article id="post-<?php the_ID(); ?>"  class="testimonial">
	<div class="jr-post-thumbnail"><?php the_post_thumbnail(); ?></div>
	<header class="entry-header">
		<?php the_title( '<h2 class="jr-post-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' ); ?>
	</header><!-- .entry-header -->
</article><!-- #post-## -->

Test the archive

Make sure all files are saved, and point your browser to http://yoursite/testimonials. You should see your post. It may look ugly – you’ll need to create some CSS to control how the post displays, but essentially the content should be there, with a clickable link. (which goes nowhere at the moment).

Create a page to display single Testimonial posts.

In the same way as we created the archive/archive content files, we’ll create a single/single content file structure.

Create a new PHP file and save it as single-testimonial.php in siteroot/wp-content/themes/child-theme-directory/

Add the following code. Note that the get_template_part() method is looking for a file in the same subdirectory as before.

 * Single Testimonial Post
 * @package Theme Name

get_header(); ?>

<div id="primary" class="content-area">
    <?php while ( have_posts() ) : the_post(); ?>
        <div class="jr-single-testimonial" role="main">
                <?php get_template_part( 'template-parts/testimonial/single', get_post_format() ); ?>    
        </div><!-- #content -->
    <?php endwhile; ?>
</div><!-- /primary -->
<?php get_footer(); ?>

Create the Single Testimonial Post content.

This one’s a little more complex, as it will be referencing the Custom Fields that we created earlier.  Start by creating a new PHP file and saving it as single.php in siteroot/wp-content/themes/child-theme-directory/template-parts/testimonial.

Add the following code. The way this works is by only adding content to the page if it has been entered on the Testimonial post.  You don’t have to use all the fields. In this case, I’ve asked for the email address in the post entry, but have decided not to actually display it at this time.

The way to check whether there’s content in the post entry for a specific field (for instance, the ‘message’ field) is by using get_field(‘message’) within a conditional statement. This means that if there’s no content, the the_field(‘message’) won’t display in the paragraph.

 * Template part for displaying testimonial single posts
 * @package Theme Name
 * @since 1.0
 * @version 1.0


<article id="post-<?php the_ID(); ?>" class="testimonial">
	<div class="jr-post-thumbnail"><?php the_post_thumbnail(); ?></div>

	<div class="jr-testimonial-content">
		<?php if(get_field('first_name')) { ?>
			<div class="jr-txtfield"><h3 class="jr-label">Name:</h3><p><?php the_field('first_name'); ?> <?php the_field('last_name'); ?></p></div> 	
		<?php } ?>		

		<?php if(get_field('testimonial_type')) { ?>
			<div class="jr-txtfield"><h3 class="jr-label">Testimonial Type:</h3><p><?php the_field('testimonial_type'); ?> </p></div> 	
		<?php } ?>

		<?php if(get_field('company_name')) { ?>
			<div class="jr-txtfield"><h3 class="jr-label">Company Name:</h3><p><?php the_field('company_name'); ?> </p></div> 	
		<?php } ?>

		<?php if(get_field('job_type')) { ?>
			<div class="jr-txtfield"><h3 class="jr-label">Job Type:</h3><p><?php the_field('job_type'); ?> </p></div> 	
		<?php } ?>

		<?php if(get_field('rating')) { ?>
			<div class="jr-txtfield"><h3 class="jr-label">Rating: <?php the_field('rating'); ?>/5</h3></div> 	
		<?php } ?>

		<?php if(get_field('message')) {  ?>
			<div class="jr-message"><h3 class="jr-label">Comments:</h3><p><?php the_field('message'); ?></p></div>
		<?php } ?>
	</div><!-- /.jr-testimonial-content -->
</article><!-- #post-## -->

Once you’ve placed all the code and saved, browse again to the archive and then click a link. Voila – there’s your post. Now all you have to do is set up some CSS to make it look nice.

What’s next?

In the next exercise, I’ll show you how to use Formidable Forms to make a front-end form which will create a Testimonial custom post type for you. Stand by!