Tania Rascia Web Design and Development

Skip Navigation
WordPress Theme Development: Pagination, Comments, Functions, & Custom Posts

WordPress Theme Development: Pagination, Comments, Functions, & Custom Posts

/ 190 responses

In part one of the tutorial series Developing a WordPress Theme From Scratch, we learned what WordPress is, what it can do, and how to do the following:

  • Set up a local Apache, MySQL, PHP (*AMP) environment
  • Install WordPress on your local server
  • Take an existing static HTML & CSS site and make it into a custom WordPress theme
  • Divide your theme into sections (index.php, header.php, footer.php, sidebar.php, and content.php)
  • Use The Loop to make posts and pages
  • Migrate local WordPress site to a live server

We created this theme using Bootstrap, and more specifically the official generic blog template. While the theme could use custom CSS or any framework, I went with Bootstrap so that we can focus on creating the theme’s function without worrying about the style.

Here is what the theme looked like at the end of the last article:

Very simple, but it effectively demonstrates how to use the WordPress Loop to display content dynamically.

In this article, we’re going to go through more essential WordPress theming techniques.


  • Basic knowledge of HTML and CSS
  • Ability set up WordPress and make a basic theme (covered in part one)


  • Create individual post pages – single.php
  • Add pagination
  • Include comments
  • Learn how to use functions.php
  • Properly enqueue stylesheets and scripts
  • Create global custom fields
  • Create custom post types

First, I’m going to start off by adding individual blog posts and pagination.

Make sure before starting this article you go to Settings > Permalinks. By default, WordPress is set to Day and name. Change this to Post name.

Individual Post Pages

In the last article, we made header, footer, sidebar, content, and page files. Now we’re going to make single.php, which is an individual post page. It’s going to be an exact duplicate of page.php, except I’m going to change 'content' to 'content-single'.

<?php get_header(); ?>

	<div class="row">
		<div class="col-sm-12">

				if ( have_posts() ) : while ( have_posts() ) : the_post();
					get_template_part( 'content-single', get_post_format() );
				endwhile; endif; 

		</div> <!-- /.col -->
	</div> <!-- /.row -->

<?php get_footer(); ?>

Now you’ll create content-single.php, which is a duplicate of content.php.

<div class="blog-post">
	<h2 class="blog-post-title"><?php the_title(); ?></h2>
	<p class="blog-post-meta"><?php the_date(); ?> by <a href="#"><?php the_author(); ?></a></p>
 <?php the_content(); ?>
</div><!-- /.blog-post -->

So now you can see that index.php is pulling in content.php, and single.php is pulling in content-single.php.

Going back to the original content.php, we have the title of each article.

<h2 class="blog-post-title"><?php the_title(); ?></h2>

Using the_permalink(), we’re going to link to the single page.

<h2 class="blog-post-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>

Now you have a blog posts on the main page that are linking individual blog post page.

Finally, we’ll want to change the_content() to the_excerpt() on content.php. The excerpt will only show the first 55 words of your post, instead of the entire contents.

<?php the_excerpt(); ?>


In the original Bootstrap blog example, there is pagination to be able to click through multiple pages if you have many blog posts.

Currently, your index.php file looks like this.

<?php get_header(); ?>

	<div class="row">
		<div class="col-sm-8 blog-main">

			if ( have_posts() ) : while ( have_posts() ) : the_post();
				get_template_part( 'content', get_post_format() );
			endwhile; endif; ?>

		</div>	<!-- /.blog-main -->
		<?php get_sidebar(); ?>
	</div> 	<!-- /.row -->

<?php get_footer(); ?>

If you’ll notice, the loop has if and while, then later endif and endwhile. To insert pagination, we’ll have to put it after the endwhile but before the endif. This means that it won’t repeat for each loop, but will only show up once based on posts.

Pagination links are called like this:

<?php next_posts_link( 'Older posts' ); ?>
<?php previous_posts_link( 'Newer posts' ); ?>

In index.php, between endwhile; and endif;, I’m going to place this code. Make sure to open and close the <?php ?> tags.

	<ul class="pager">
		<li><?php next_posts_link( 'Previous' ); ?></li>
		<li><?php previous_posts_link( 'Next' ); ?></li>

By default, 10 posts will show up on a page before it will link to another page. For testing purposes, I’m going to go to Settings > Reading and change Blog pages show at most to 1.

Now we have functioning pagination.


One of the biggest advantages WordPress and server based content management systems have over static site generators is the ability to include comments without using a third party. (However, static site generators have many more advantages – I have an article on setting up Jekyll if you’re interested in learning how to use them.)

Comments seem complicated to set up, but it doesn’t have to be hard at all. First, we’re going to go back to single.php and enable the comments.

Right now, the code looks like this.

if ( have_posts() ) : while ( have_posts() ) : the_post();
	get_template_part( 'content-single', get_post_format() );
endwhile; endif; 

We’re going to change it to look like this.

if ( have_posts() ) : while ( have_posts() ) : the_post();
	get_template_part( 'content-single', get_post_format() );

	if ( comments_open() || get_comments_number() ) :

endwhile; endif; 

This is just telling the single post to display the comments template. Now we’ll create comments.php.

<?php if ( post_password_required() ) {
} ?>
	<div id="comments" class="comments-area">
		<?php if ( have_comments() ) : ?>
			<h3 class="comments-title">
				printf( _nx( 'One comment on “%2$s”', '%1$s comments on “%2$s”', get_comments_number(), 'comments title'),
					number_format_i18n( get_comments_number() ), get_the_title() );
			<ul class="comment-list">
				wp_list_comments( array(
					'short_ping'  => true,
					'avatar_size' => 50,
				) );
		<?php endif; ?>
		<?php if ( ! comments_open() && get_comments_number() && post_type_supports( get_post_type(), 'comments' ) ) : ?>
			<p class="no-comments">
				<?php _e( 'Comments are closed.' ); ?>
		<?php endif; ?>
		<?php comment_form(); ?>

Comments are not the simplest part of WordPress theming, but I’ve managed to reduce it down to a small enough code block.

First, we’re setting functionality to prevent users from posting comments if you’ve set your settings to password protected comments (post_password_required()). Then we’re creating a comments div, and if there are comments (have_comments()), it will display how many comments there are on the post (get_comments_number()), followed by the list of comments (wp_list_comments()). If the comments are closed (! comments_open()), it will let you know; at the end will be the form to submit a comment (comment_form()).

Without adding any styles, here is how the functioning single blog post looks.

Obviously the styles aren’t quite there yet, but I don’t want to focus on that in this article. Remove the list-style on the uls, add some padding and margins and possibly some borders and background colors, and you’ll have a much prettier comment setup.

Of course, you might want to show how many comments there are or link to the comments from the main page. You can do that with this code inserted into content.php.

<a href="<?php comments_link(); ?>">
	printf( _nx( 'One Comment', '%1$s Comments', get_comments_number(), 'comments title', 'textdomain' ), number_format_i18n( 						get_comments_number() ) ); ?>

Now that we have pagination, blog posts, and comments set up, we can move on to functions.

Using and Understanding the WordPress Functions File

Located in your theme directory, you can create a file called functions.php. You can use functions.php to add functionality and change defaults throughout WordPress. Plugins and custom functions are basically the same – any code you create can be made into a plugin, and vice versa. The only difference is that anything you place in your theme’s functions is only applied while that theme is actively selected.

I have a README on GitHub of useful WordPress functions, which might come in handy the more you use them.

functions.php seems complicated, but it’s mostly made up of a bunch of code blocks that, simplified, look like this:

function custom_function() {
add_action( 'action', 'custom_function');

So, we’re creating our custom function, and adding it in based on action references. Within this file, you can pretty much change or override anything in WordPress.

Let’s go ahead and make functions.php and place it in our theme directory.

Since it’s a PHP file, it needs to be begin with the opening PHP tag. It doesn’t need a closing tag; pure PHP files don’t need closing tags.


Eventually, you can insert these types of functions into your own custom plugin that can be used across many themes, but for now we’ll learn how to do it in the theme specific file.

Enqueue Scripts and Stylesheets

By the end of the last article, I was incorrectly linking to my CSS and JavaScript in the header and footer, respectively. This should be done through the functions file.

First, delete the links to the stylesheets and scripts that you have in your header and footer. They’re no longer going to be hard coded into the theme.

I’m going to make css, js and images directories in the root of my theme. So here’s what I have:

  • css
    • bootstrap.min.css
    • blog.css
  • js
    • bootstrap.min.js

Now here’s the first code block we’re going to put in functions.php:

// Add scripts and stylesheets
function startwordpress_scripts() {
	wp_enqueue_style( 'bootstrap', get_template_directory_uri() . '/css/bootstrap.min.css', array(), '3.3.6' );
	wp_enqueue_style( 'blog', get_template_directory_uri() . '/css/blog.css' );
	wp_enqueue_script( 'bootstrap', get_template_directory_uri() . '/js/bootstrap.min.js', array( 'jquery' ), '3.3.6', true );

add_action( 'wp_enqueue_scripts', 'startwordpress_scripts' );

In order for these to properly be inserted into your theme, <?php wp_head(); ?> needs to be placed before the closing </head> tag, and <?php wp_footer(); ?> before the closing </body> tag.

By common WordPress convention, I’m naming my script after my theme (startwordpress_scripts()). wp_enqueue_style is for inserting CSS, and wp_enqueue_script for JS. After that, the array contains the ID, location of the file, an additional array with required depenedencies (such as jQuery), and the version number.

Now we have jQuery, Bootstrap CSS, Bootstrap JS, and custom CSS being properly loaded into the website.

Enqueue Google Fonts

The function to include the Google Fonts stylesheets is slightly different, based on the dynamic nature of the URL. Here is an example using Open Sans.

// Add Google Fonts
function startwordpress_google_fonts() {
				wp_register_style('OpenSans', 'http://fonts.googleapis.com/css?family=Open+Sans:400,600,700,800');
				wp_enqueue_style( 'OpenSans');

add_action('wp_print_styles', 'startwordpress_google_fonts');

Now I have Open Sans by Google Fonts linked in my page.

Fix the WordPress Title

If you’ll notice, we’re currently pulling in the title for the website with this code.

<title><?php echo get_bloginfo( 'name' ); ?></title>

This is not very intuitive – it means that whatever you have set as your website’s title will be the title tag for every page. However, we’re going to want each individual page to show the title of the article first, and also include a reference to the main site title.

Introduced in WordPress 4.1 is the ability to simply have WordPress take care of the title tag in an intuitive way. Simply remove the title tag from your header.php entirely, and in functions.php, add this code block.

// WordPress Titles
add_theme_support( 'title-tag' );

Create Global Custom Fields

Sometimes, you might have custom settings that you want to be able to set globally. An easy example on this page is the social media links on the sidebar.

Right now these links aren’t leading anywhere, but we want to be able to edit it through the admin panel. The source of this code is modified from this Settings API tutorial.

First, we’re going to add a section on the left hand menu called Custom Settings.

// Custom settings
function custom_settings_add_menu() {
  add_menu_page( 'Custom Settings', 'Custom Settings', 'manage_options', 'custom-settings', 'custom_settings_page', null, 99 );
add_action( 'admin_menu', 'custom_settings_add_menu' );

Then we’re going to create a basic page.

// Create Custom Global Settings
function custom_settings_page() { ?>
  <div class="wrap">
    <h1>Custom Settings</h1>
    <form method="post" action="options.php">
           settings_fields( 'section' );
           do_settings_sections( 'theme-options' );      
<?php }

The code contains a form posting to options.php, a section and theme-options, and a submit button.

Now we’re going to create an input field for Twitter.

// Twitter
function setting_twitter() { ?>
  <input type="text" name="twitter" id="twitter" value="<?php echo get_option( 'twitter' ); ?>" />
<?php }

Finally, we’re going to set up the page to show, accept and save the option fields.

function custom_settings_page_setup() {
  add_settings_section( 'section', 'All Settings', null, 'theme-options' );
  add_settings_field( 'twitter', 'Twitter URL', 'setting_twitter', 'theme-options', 'section' );

  register_setting('section', 'twitter');
add_action( 'admin_init', 'custom_settings_page_setup' );

Now I’ve saved my Twitter URL in the field.

For good measure, I’m going to add another example, this time for GitHub.

function setting_github() { ?>
  <input type="text" name="github" id="github" value="<?php echo get_option('github'); ?>" />
<?php }

Now you’ll just duplicate the fields in custom_settings_page_setup.

  add_settings_field( 'twitter', 'Twitter URL', 'setting_twitter', 'theme-options', 'section' );
  add_settings_field( 'github', 'GitHub URL', 'setting_github', 'theme-options', 'section' );
	register_setting( 'section', 'twitter' );
  register_setting( 'section', 'github' );

Now back in sidebar.php, I’m going to change the links from this:

<li><a href="#">GitHub</a></li>
<li><a href="#">Twitter</a></li>

To this:

<li><a href="<?php echo get_option('github'); ?>">GitHub</a></li>
<li><a href="<?php echo get_option('twitter'); ?>">Twitter</a></li>

And now the URLs are being dynamically generated from the custom settings panel!

Featured Image

You might want to have a featured image for each blog post. This functionality is not built into the WordPress core, but is extremely easy to implement. Place this code in your functions.php.

// Support Featured Images
add_theme_support( 'post-thumbnails' );

Now you’ll see an area where you can upload an image on each blog post.

I’m just going to upload something I drew in there for an example. Now, display the image in content-single.php.

<?php if ( has_post_thumbnail() ) {
} ?>

Now you have an image on your individual post pages! If you wanted the thumbnail to show up on on the main blog page as well, you could do something like this on content.php to split the page if a thumbnail is present:

<?php if ( has_post_thumbnail() ) {?>
	<div class="row">
		<div class="col-md-4">
			<?php	the_post_thumbnail('thumbnail'); ?>
		<div class="col-md-6">
			<?php the_excerpt(); ?>
	<?php } else { ?>
	<?php the_excerpt(); ?>
	<?php } ?>

Custom Post Types

One of the most versatile way to extend your WordPress site as a full blown content management system is with custom post types. A custom post type is the same as Posts, except you can add as many of them as you want, and with as much custom functionality as you want.

If you’re interested in using plugins, you can download the Advanced Custom Fields plugin, which will add a great deal of customizability to your theme with little effort.

For now, I’m going to show you how to set up a simple custom post type, and call the post in it’s own loop. There is much more that can be done with custom post types, but that’s a bit more complicated and deserves an article all of its own.

Custom Post Types on the WordPress codex will also give you more insight on some of the possibilities available.

In functions.php, I’m going to create the custom post type called My Custom Post.

// Custom Post Type
function create_my_custom_post() {
	register_post_type( 'my-custom-post',
			'labels' => array(
					'name' => __( 'My Custom Post' ),
					'singular_name' => __( 'My Custom Post' ),
			'public' => true,
			'has_archive' => true,
			'supports' => array(
add_action( 'init', 'create_my_custom_post' );

In the create_my_custom_post(), I’ve created a post called My Custom Post with a slug of my-custom-post. If my original URL was example.com, the custom post type would appear at example.com/my-custom-post.

In supports, you can see what I’m adding – title, editor, thumbnail, and custom fields. These translate to the fields on the back end that will be available.

  • title is the title field that I call with <?php the_title(); ?>.
  • editor is the content editing area that I call with <?php the_content(); ?>.
  • thumbnail is the featured image that I call with <?php the_post_thumbnail(); ?>.
  • custom-fields are custom fields that I can add in and call later.

I’ve decided I’m going to make a new page for the custom post to loop in. I created a page called Custom, which will appear at example.com/custom. Right now, my page is pulling from page.php, like all the other pages.

I’m going to create page-custom.php, and copy the code over from page.php. According to the WordPress template hierarchy, a page-name.php will override page.php.

The original loop we used looked like this:

if ( have_posts() ) : while ( have_posts() ) : the_post();
	// Contents of the Loop
endwhile; endif; 

A custom post type loop will look like this:

$custom_query = new WP_Query( $args );
while ($custom_query->have_posts()) : $custom_query->the_post();
  // Contents of the custom Loop

Note that this only a while, and does not have an if or endif.

I’ll have to define the $args or arguments, before the loop.

$args =  array( 
	'post_type' => 'my-custom-post',
	'orderby' => 'menu_order',
	'order' => 'ASC'

Here I’m defining the post type as my-custom-post, and ordering the posts in ascending order.

So here’s the entire code for page-custom.php.

<?php get_header(); ?>

	<div class="row">
		<div class="col-sm-12">

				$args =  array( 
					'post_type' => 'my-custom-post',
					'orderby' => 'menu_order',
					'order' => 'ASC'
				 $custom_query = new WP_Query( $args );
            while ($custom_query->have_posts()) : $custom_query->the_post(); ?>

				<div class="blog-post">
					<h2 class="blog-post-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
					<?php the_excerpt(); ?>

				<?php endwhile; ?>
		</div> <!-- /.col -->
	</div> <!-- /.row -->

	<?php get_footer(); ?>

Now example.com/custom will only pull in posts from the custom post type we created. Right now, the custom post type is set up to only do things that the normal posts can do, but the more you fall down the rabbit hole, the more possibilities you discover. If this isn’t working, make sure you’ve successfully updated permalinks.


We covered a lot of ground in this article; you should now be able to…

  • Create individual pages for blog posts
  • Add pagination to a blog
  • Add comments to a blog
  • Add WordPress functions
  • Enqueue scripts and stylesheets properly
  • Have a proper page title
  • Create custom global settings
  • Create a custom post type

All the source code is on GitHub as always. You can download the code and upload it into your themes folder and see it in action. I might set up a demo if there’s any interest in that.

There is much more that custom post types can do – and much more that WordPress can do, as well. I haven’t touched on the theme customizer, which is the most current way of creating themes.

I plan to continue this WordPress series and expand upon custom post types and what they can do. For now, I hope you learned something new, and of course, please feel free to reach out if I made any mistakes or errors or included any wrong or outdated information.

View on GitHub

Part Three

Update 8/11/16

In part three, I discuss how to add custom fields and meta boxes to a post!

Go to Part 3

Thank you for reading! I'm Tania Rascia, and I write no-nonsense guides for designers and developers. If my content has been valuable to you, please help me keep the site ad-free and continuously updated with quality tutorials and material by making a donation. Any amount helps and is greatly appreciated! Otherwise, let me know any ideas you have on a course you'd be eager to see.

Write a response

Your email address will not be published.


  • Jonathan says:

    I got completely lost after the function file was introduced 🙁

  • Kevin Conklin says:

    Again, setting the link to permalinks gives me a 403 forbidden… I have followed the steps exactly and I still dont know why this happens. Please help!!

    • Kevin Conklin says:

      UPDATE: I had to delete the HTACCESS file to get back into the dashboard. BUT, when I go to add the permalink back to get a new HTACCESS file…403 AGAIN! Over and over and over. Tania?!

      • Kevin Conklin says:


      • Tania says:

        Hey there. I’m currently backpacking in Budapest so haven’t been able to get to anything immediately. This is part two of the series, and changing your updating your permalinks should already be completed before even arriving at this article. Without knowing more information, I don’t know how to troubleshoot your issue. Have you made sure your htaccess file is set to 666 or higher permissions? Here is an article that should address any issue.

  • Rhan says:

    Hi thanks for this, I’m new to creating sites with WordPress and your tutorials help me a lot.

  • Wordpress Programmer says:

    This was a great article with lots of good examples and resources. It is really helpful. Thank you very much.

  • Cristian says:

    Great tutorial.

    Check my result here https://goo.gl/tS4BZ1

  • j0rdnh says:

    Hi Tania great tutorial but there’s one thing i can’t figure out…

    The navigation carat that points to the active page doesn’t show up at all once the navigation is updated to reflect the actual pages of the site.

    In blog.css, the carat is associated with the class “active” but there is no “active” class in header.php after the update (or anywhere else). Why is that part of the css still there and how can I designate the active page to show the carat?


  • Matthew says:

    very informative tutorial for wp newbies!! 🙂 Thank you.

  • Rene says:

    // Add scripts and stylesheets
    function startwordpress_scripts() {

    wp_enqueue_script( ‘bootstrap’, get_template_directory_uri() .
    hello i have write in my functions php

    ‘/js/bootstrap.min.js’, array( ‘jquery’ ), ‘3.3.6’, true );

    wp_enqueue_style( ‘bootstrap’, get_template_directory_uri() . ‘/css/bootstrap.min.css’, array(), ‘3.3.6’ );

    wp_enqueue_style( ‘blog’, get_template_directory_uri() . ‘/css/blog.css’ );


    but wordpress better bootsrap make a line- height to body so that i have white space over the navigation when link rel is hardcoding all show correctly how can i fix that greetings from berlin

  • R.B. says:

    Hey Tania a while i experiemented on my own theme development with wordpress your tutorial is very helpfull but im dont know how bootstrap set a line height to body and how i can overwrite this in blog.css
    her is a excerpt from my functions php what can i do then before hardcoding link rel this was all normal now i have a little white space over the menu

    / Add scripts and stylesheets
    function startwordpress_scripts() {

    wp_enqueue_script( ‘bootstrap’, get_template_directory_uri() . ‘/js/bootstrap.min.js’, array( ‘jquery’ ), ‘3.3.6’, true );
    wp_enqueue_style( ‘bootstrap’, get_template_directory_uri() . ‘/css/bootstraprespo.css’, array(), ‘3.3.5’ );
    wp_enqueue_style( ‘bootstrap’, get_template_directory_uri() . ‘/css/bootstrap.min.css’, array(), ‘3.3.6’ );

    wp_enqueue_style( ‘blog’, get_template_directory_uri() . ‘/css/blog.css’ );
    I hope that you can hel me to fix this
    greetings from Berlin

  • Ricardo Mota says:

    Hey Tania
    Great tutorial. Struggled here and there but always managed to sort it. There is one thing that I still don’t know where to find.

    add_theme_support( ‘title-tag’ );

    Where did the titlle tag go? I assumed there should be a setting or filed in each type of post (page, media, post etc) but I can’t find it.

    Keep up the good work. Off to part 3?. Wish me luck.

    • Matthew says:

      Could be wrong on what you’re asking but all you need to do is add ‘add_theme_support( ‘title-tag’ );’ to your functions.php.

      Then remove title tag from your header.php file completely 🙂

      You do not need to add it to separate pages/posts.

  • jonny says:

    Thank you for this Tutorial, you are my hero 🙂

  • Irfan Shaikh , Dewas , India says:

    Hi Tania
    I faced a lot of problem in part-II created by myself only , but I solved them throughout the night and successfully completed part-II. It cleared all my basic concepts of wp.
    really u r great! Now I have started loving word press.
    Thanks by heart.

  • Irfan Shaikh says:

    Hi Tania,
    While I was making wordpress website from scratch following part-I of your tutorial everything was in order as you shown. When I started part-II , my web page is showing pages only not the post. How to get posts on web page?
    Please guide

  • Maxima Rosekrans says:

    Many thanks really handy. Will share site with my good friends.|


  • Spencer Williams says:

    This is awesome, thanks so much, I love you!!!

  • Rob says:

    Hi Tania! Thank you for share this knowledge with us. I just wanted to know if you could share the files (index.php, page.php, and so on) Because I tried to follow you but I get lost and I don’t know where could I find the wrong line…

    Thank you in advance.

  • Grant says:

    I figured it out the permalink at the top of the post form needs to be edited. I was looking for it in the code. Newbie mistake there.

  • Grant says:

    My custom post is not showing. I’m not sure what you mean by making sure to updated my permalinks.
    What should they be updated with?

    Great tutorial, I never seem to find a theme that I’m happy with so I decided maybe to make my own. I stumbled on to you tutorial. I’m glad I did.

  • Jeff says:

    Meant to post there here in Part 2 not Part 1.

    Having some trouble getting the pagination working, any ideas?

    Apparently you can’t post a code example here or I just don’t know how to.

    Screenshot of what I have that isn’t working:

  • Lucia says:

    I got lost from “Enqueue Scripts and Stylesheets”, where I have to link the file functions.php to the bootstraps ones. the ones downloaded do not have much text in them and when I upload my page it clearly shows that it is not reading the css file….

    • Tania says:

      Hi Lucia,

      If you’re having issues with the files loading in, look at the front end of your website, right click and select “View Source” and look at what URL is being pulled in for the stylesheets. The most common error is having the .css file in a different directory from the one that’s loading.

    • Ismi says:

      I lost where to plug the function startwordpress_scripts() which is created in functions.php file.
      I added in header.php and it worked. Hope this helps.

      • Tania says:

        You need <?php wp_footer(); ?> and <?php wp_head(); ?> in your header and footer. Do not directly include that code.

  • Codebrain says:

    Thanks heaps. I really like your way of teaching. Please do one on WP Plugin.

  • vallary wanjohi says:

    Great tutorial Tania.

  • Steve says:

    Is this tutorial a page or a post?

  • Samuel Becker says:

    I want to thank you so much for this guide.
    Made the initially daunting task smooth and simple : )

  • Aman Saxena says:

    When I applied the entire procedure, menu section in Appearance(Appearance>Menus) aren’t showing.
    I can only manage my pages from theme customise section in the Appearance>Themes

  • I may be way off here as I’m really new to WP but when creating custom post types you’ve said in order to display them differently I have to create page-[custom-post-type].php and write the code to display them on there with:
    $args = array(
    'post_type' =&gt; 'my-custom-post',
    'orderby' =&gt; 'menu_order',
    'order' =&gt; 'ASC'
    $custom_query = new WP_Query( $args );

    But that didn’t seem to work for me. I looked into the WP Codex and it says the hierarchy would look for archive-[custom-post-type].php

    Is there a reason you’ve used page rather than archive? I’m a bit confused 🙁

    Love these tutorials BTW! They’re by far the best I’ve found!

    • Tania says:

      The Page has nothing to do with the custom post type. You can make page-my-portfolio.php, and make a page under Pages called My Portfolio, with the slug my-portfolio. The URL (slug) of this page must match the actual page you make in the back end. So example.com/my-portfolio will be reached with page-my-portfolio.php AND a Page must be made in the back end.

      You can include a custom post type called “work-examples”, and include that in your “My Portfolio” page. The custom post type has nothing to do with the page.

  • tagwolf says:

    If anyone is wondering on basics for comment css formatting in this tutorial. I did the following. (Took me a second to figure out indenting of reply comments):

    * Comments

    .comments-area ul {
    list-style: none;
    margin: 0px;
    padding: 0px;

    .comments-area ul.children {
    padding-left: 30px;

    .comment-body {
    border: 1px solid #ddd;
    background-color: #f5f5f5;
    padding: 5px;
    margin: 5px;

  • Anonymous says:

    Did u miss function custom_settings_page(){} line on purpose? So that people see the original tut. lol. just reminding. This is awesome tut. thank you.

  • GJ says:

    First of all, Amazing tutorial! I have learned so much so far. I did stumble upon a little problem however.

    I have followed the tutorial exactly, but everytime I press the title of a blog post in order to go to the blog post page, the browser cannot find the page for some reason. Did anybody have the same problem as well??

  • Tim says:

    I meant; you have said to show how many comments there are or link to the comments from the main page. You can do that with this code inserted into content.php.

    Where do you paste/insert this code?

    <a href="<?php comments_link(); ?>">
    printf( _nx( ‘One Comment’, ‘%1$s Comments’, get_comments_number(), ‘comments title’, ‘textdomain’ ), number_format_i18n( get_comments_number() ) ); ?>

    Sorry for not escaping the code earlier. 🙁

  • Lucky says:

    Hello, thanks for this great tutorial for beginners like myself. I have so far followed the tutorial step by step and using the same html code source as you, however when I enqueue styles and scripts, the whole function code appear on the front page of the wordpress site…I do not understand what is happening.

    • Lucky says:

      I have figured out what was the problem. I forgot to start the function with <?php tag…what a silly mistake that had me hitting a wall with my head.

  • Steven says:

    Follow up to my last comment. example.com/custom wasn’t working for me so I guess I’ve been accessing the single post view for the post. I’ll look into it in the AM…

  • Steven says:

    This tutorial has been fantastic so far! I’ve went through other ones before and found lots of mistakes in them. I only found one mistake in this one, it’s very minor and is not a big deal, however I would like to point it out in case anyone else gets stuck during this step:

    When I created a custom post type using the code provided, I couldn’t access it from example.com/custom. I could only access it from example.com/my-custom-post/


  • Joel says:

    Your code for the contact form in this tutorial when you are logged out asks for an email. Is there a way to make it display a log-in button?

  • Trine says:

    Hi Tania! Great tutorial – working my way through it. However i am not stuck. This code:
    <ul class="pager">
    <li><?php next_posts_link( ‘Previous’ ); ?></li>
    <li><?php previous_posts_link( ‘Next’ ); ?></li>

    makes my site go blank! Please help!

    ps. hope I did the pre code and escape thingy correctly!

    • mic says:

      Hi Trine! I just had the same problem – placing this code after

      endwhile; endif;

      seems to have solved this for me. Full code looks like:

    • mic says:

      close like this: endwhile; ?>

      and than after the pasted nav re-open and close again :

    • mic says:

      sorry, my corrected answer does not show….: close php after “endwhile;” -> re-open again before “endif;” and then close again….

      • Jeff says:

        Hi mic,

        I still can’t seem to get this working, I tried what you said and the nav doesn’t display. Here’s my code:

Tania's List

My tutorials, guides, and articles for web designers, developers, and autodidacts, sent out once a month or so. No bullshit, ads, or tricks.