LinkedIn Facebook Twitter


WordPress 3 Custom Post Types : Create a simple Portfolio Page

March 2nd, 2011 | Php , Tips and Tricks , Tutorials , Wordpress

I am working on a Wordpress theme lately and just finished coding the Portfolio page. I would like to share my experience with Custom Post Types with you. (Thanks to Joe Casabona, his post is a great start guide.)

Ok, less talk, more code....

We want to...

  1. Add "Client" and "Short Text" fields to our work
  2. Add "Types" so our work can be filtered
  3. Add Sorting Function to Portfolio List (by Date, Project name and Client)
  4. Add Filtering function to theme
  5. Have Pagination on the page


Create Custom Post Type

add_action('init', 'gs_portfolio_register');//Always use a shortname like "gs_" not to see any 404 errors

function gs_portfolio_register(){
    $args = array(
        'label' => __('Portfolio List'),
        'singular_label' => __('Portfolio'),
        'public' => true,
        'show_ui' => true,
        'capability_type' => 'post',
        'hierarchical' => false,
        'rewrite' => array('slug' => 'project'),//Use a slug like "work" or "project" that shouldnt be same with your page name
        'supports' => array('title', 'editor', 'thumbnail')//Boxes will be showed in the panel

    register_post_type( 'gs_portfolio' , $args );

//////ADD CUSTOM INPUTS (Client & Short_text)
add_action("admin_init", "admin_init");
add_action('save_post', 'save_options');

function admin_init(){
    add_meta_box("gs_portfolioInfo-meta", "Portfolio Options", "meta_options", "gs_portfolio", "side", "low");

function meta_options(){
    global $post;
    $custom = get_post_custom($post->ID);
    $client = $custom["client"][0];
    $short_text = $custom["short_text"][0];
	<p><label>Client:</label><br /><input name="client" value="<?php echo $client; ?>" /></p>
	<p><label>Short Text:</label><br /><textarea name="short_text"><?php echo $short_text; ?></textarea></p>

function save_options(){
    if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) return $post_id;
    global $post;
    update_post_meta($post->ID, "client", $_POST["client"]);
    update_post_meta($post->ID, "short_text", $_POST["short_text"]);

//////ADD TAXONOMY FOR FILTERING (Taxonomy name: types)
register_taxonomy("types", array("gs_portfolio"), array("hierarchical" => true, "label" => "Types", "singular_label" => "Types", "rewrite" => true));

add_filter("manage_edit-gs_portfolio_columns", "gs_portfolio_edit_columns");  
add_action("manage_posts_custom_column",  "gs_portfolio_custom_columns");

function gs_portfolio_edit_columns($columns){
    $columns = array(
        "cb" => "<input type=\"checkbox\" />",
        "title" => "Portfolio Title",
        "short_text" => "Short Text",
        "client" => "Client",
        "types" => "Types",

    return $columns;

function gs_portfolio_custom_columns($column){
    global $post;
    $custom = get_post_custom();
    switch ($column)
        case "short_text":
            echo $custom["short_text"][0];
        case "client":
            echo $custom["client"][0];
        case "types":
            echo get_the_term_list($post->ID, 'types', '', ', ','');

Create a Portfolio Template

Template Name: Portfolio

<?php get_header(); ?>

<div class="sidebar grid_3 alpha">

	<div id="sort">
		<h5>SORT BY : </h5>
			<li><a href="<?php echo add_query_arg(array ('paged' => '1',  'orderby' => 'date', 'order' => 'DESC'));?>">Date</a></li>
			<li><a href="<?php echo add_query_arg(array ('paged' => '1',  'orderby' => 'title', 'order' => 'ASC'));?>">Project (A to Z)</a></li>
			<li><a href="<?php echo add_query_arg(array ('paged' => '1',  'orderby' => 'meta_value', 'order' => 'ASC', 'meta_key' => 'client'));?>">Client (A to Z)</a></li>
	<div id="filter">
		<h5>FILTER BY : </h5>
		$categories=  get_categories('taxonomy=types&title_li='); 
		foreach ($categories as $category){ ?>
		<li><a href="<?php echo add_query_arg(array ('paged' => '1',  'filter' => $category->category_nicename));?>" title="Filter by <?php echo $category->name;?>"><?php echo $category->name;?></a></li>
		<?php }?>
	<div id="reset-filters">
		<a href="<?php echo add_query_arg(array ('paged' => '1',  'filter' => ''));?>">reset filters</a>


<div id="content" class="grid_9 omega">

	<ul id="work-list">
		$query = 'post_type=gs_portfolio&types='.$_GET['filter'].'&orderby='.$_GET['orderby'].'&order='.$_GET['order'].'&meta_key='.$_GET['meta_key'].'&posts_per_page=3&paged='.$paged;
		if (have_posts()) : while (have_posts()) : the_post();
		$custom = get_post_custom(get_the_ID());  
		<li class="clearfix">
			<div class="work-thumb">
				<a title="<?php echo get_the_title(); ?>" href="<?php the_permalink(); ?>"><?php the_post_thumbnail('portfolio-img-440');?></a>
			<div class="work-details">
				<h6 class="project">Project :</h6>
				<p><a href="<?php the_permalink(); ?>" rel="bookmark" title="<?php echo get_the_title(); ?>"><?php the_title(); ?></a></p>
				<h6 class="client">Client :</h6>
				<h4><?php echo $custom["client"][0];?></h4>
				<p class="short"><?php echo $custom["short_text"][0];?></p>
				<p><a href="<?php the_permalink(); ?>">View Project →</a></p>
		<?php endwhile; ?>
		<?php endif;?>

	<div class="posts-nav">
		<div class="prev"><?php next_posts_link(__('← Older Projects')) ?></div>
		<div class="next"><?php previous_posts_link(__('Newer Projects →')) ?></div>


<?php get_footer(); ?>

View Single Project

You can duplicate and rename your single.php as single-portfolio.php if you want to have a custom single page for your projects.

Responses to "WordPress 3 Custom Post Types : Create a simple Portfolio Page"

  1. Hi,

    Thanks for sharing your experience. It was very helpful.
    is it possible to add the CSS file?

    Thanks again.

      • Thanks! It helped me a lot. I was confused as to where to place the “floats” and “clear” (I don’t know how they are called in english…).

        In your CSS file I saw that you have added all the styling for the landing page after clicking “view project”. Is it a custom page that you have created? Do you know how can I tell WordPress to go to a specific/custom page instead of the single.php?

        Thanks again for your tutorial.

  2. admin says:

    Just duplicate the single.php and rename it as “single-gs_portfolio.php” (or whatever Post Type Name you registered ). Hope it helps

  3. admin says:

    Btw, you can take a look at live example beta template : http://grattis.geryit.com .

  4. Hi,

    The thing is that I’m using Thesis Framework. So I have to adapt everything the Thesis way. I have to figure out how I can tell my portfolio page (where all my work is listed) to open in a custom-single.php (with it’s own layout). I’m sure it’s very easy but I have spent so much time on it, I probably need to get fresh air, get my mind out of it for a while… 😉

    Thanks for your time and everything.

  5. Josh says:

    Hi there,

    great tutorial, thanks for posting! I have a question though; I’ve implemented this on my website for my portfolio, and would like to copy the effect for my workshops.

    I am able to register the new custom post type, but I’m having trouble adding the custom input fields. When I copy and paste the code I get a server error, presumably because of the repeating functions. When I copy the functions I change the title from jp_portfolio… to jp_workshops…

    any suggestions?


  6. admin says:

    Thanks Josh. Can you send me the code to [email protected]?

  7. S K Mat says:

    Can you port the code as a twenty ten child theme and release it.
    That’s will be great.

  8. Andre says:

    Great tutorial and out of the hours (yes hours) of finding something, yours works…however, a couple things: First, in WordPress, it shows portfolio categories for the menu manager, so I decided to make a menu link to each portfolio catergory. But, I get 404’s. It appears depsite the portfolio category meta box showing up for menu creation, adding them to a menu doesn’t work, is there a solution? Second, when debugging my website (wordpress debug turned on, your filter shows this:
    Notice: Undefined index: filter in ….”path here and line it’s on”. It shows this for each of the filter, orderby, order, and meta key. Any idea how to solve this?
    Thanks in advance.

    • admin says:

      Hey Andre, thanks. Thats a very common problem. Just go to permalinks page and hit the save changes button. Your permalink settings will be refreshed. For other issue, use isset for vars. Like if (isset($filter)){}.

  9. Abe says:

    Hi. Thank you for this.
    I would like to incorporate your ‘sort’ solution by putting the code in a php-widget.

    The custom post type = literature.
    I would like to give my users the opportunity to sort the literature by:
    – date posted
    – title descending
    – publicationyear (which is a custom taxonomy)

    Do you know how I can do this?
    I tried using and customising your piece of code, but did not succeed.


    • admin says:

      Hi Abe,
      so your problem is you cant make this work in a widget but you successfully made it work as a single project page?

  10. Andre says:

    I discovered an problem…I always have 5 portfolio items showing. Doesn’t matter if I have 10 or 20 items in one category, it’s always 5 showing. Any idea why it’s always 5 instead of all or any specified number?

    • admin says:

      Probably you didnt set post_per_page value so it show 5 posts (default wp post per page value) . Can you change “Blog pages show at most” value from the admin panel. Its under Settings>Reading Settings

      • Andre says:

        Actually, I had customized the portfolio page and something about my changes is affecting it (still not sure why everything works but it always stays at 5 and don’t know what the significance of 5 is. I’ve added a thumbnail resizer, and also did a 3 column layout but I did set the post per page at -1, then tried 9, etc, still 5 shows. So when I put your code as-is back in, it works. I just need to add my own additions one at a time and remake what I did beore and see what is causing it to do 5 posts. A challenge considering I’m not a programmer.

        • Andre says:

          ah..just realized what you said about the 5 posts is default. So it means whatever I did with my own coding modifications that the posts per page is being ignored in this page and it reverts to the WP default 5.

        • admin says:

          Wish I could help you dude :/

          • Andre says:

            Amazing what going for coffee does…I solved the problem and also got my own modifications working 100% in conjunction with your portfolio type. Looks good and now I can do 1, 2, 3, or 4 column layouts. Cheers!

  11. Magdy says:

    First of all, thanks for sharing your experience.
    It cleared a lot of doubts on filtering the content by category.

    What I didn’t manage to get is if I have a subcategory. I would like the subcategory to appear only when the category is selected.
    Who to get this? I tried to check if category has a subcategory, an if statemente on the Page template but nothing.

    Any idea on how to get this?
    Thanks in advance!

    • admin says:

      Hey Madgy,
      can you send me your code to [email protected]?

      • Magdy says:


        Thanks for answering.
        Actually the code I’m using is pretty much the same as yours. Changes I made were only in the name of cutsom post-type.

        What a tried to do, was in functions.php: I tried to check if there are subcategories, using this code:
        function category_has_children() {
        global $wpdb;
        $term = get_queried_object();
        $category_children_check = $wpdb->get_results(” SELECT * FROM wp_term_taxonomy WHERE parent = ‘$term->term_id’ “);
        if ($category_children_check) {
        return true;
        } else {
        return false;

        Then I added parent=0 to this line (to hide the subcategories):
        $categories= get_categories(‘taxonomy=types&title_li=’);
        plus this if statement after the foreach loop on template-portfolio.php:
        if (!category_has_children()) {
        echo 'Sorry'!
        } else {
        <a href=" ‘1’, ‘filter’ => $category->category_nicename));?>” title=”Filter by name;?>”>name;?>

        I’m pretty sure I’m missing something…
        Also I’m not an wordpress expert…
        Well this is the code I’m using + your code.

        Hope you can help or give some light on this.
        Thanks, and Happy 2012.

  12. Magdy says:

    Something on these? Anything…
    Thanks man.

  13. Andre says:

    Guess who! lol…quick question, I’m still getting used to this custom post type thing with WordPress. The portfolio I put together works great, but when I click on a portfolio item to view it in a single-portfolio.php page, I noticed the menu link to the portfolio page is no longer active, the Blog menu link at the top is now active, but the breadcrumbs navigation shows the correct path. What would cause my blog menu item to suddently become active?

    • admin says:

      Hey Andre, do you have on online sample for this exercise?

      • Andre says:

        First up, not sure if this is a WordPress issue but it appears a ton of people have 404’s with custom post types and the method to go to the permalinks settings, save, still does not work for me here. In my demo site, under Images, is a menu for the portfolio and a submenu for that going to a category of the portfolio “Happiness”. I get 404’s. But also the issue of the blog link being active when viewing a portfolio item is what is confusing me. I also (testing) added a submenu link on the Portfolio one as “Sleeping is Great” which is a portfolio item. Watch what happens to the top main menu active links.

        Site: http://wordpress-themes.gradientpixels.ca/incantation-pro/?page_id=17

        So the hard part is to try and determine what the Portfolio type and Portfolio_Category (taxonomy) does and what part is related to WP core.

  14. Andre says:

    Well it was 4 days of nightmare hell, but it looks like the issues I posted are actually common with many in relation to custom post types, viewing an item from one, and the active blog menu item (when doing a static front page method), where the blog menu goes active. This points to a WordPress bug and is still existing even with WP 3.3. I would imagine it’s also a WP bug that gives the 404’s on the portfolio category menu links. Long story short, the portfolio works well but the WordPress core has bugs.

  15. Henry says:

    Thanks a lot for your sharing !

    I spend 3 days to fight with 404 error but it was my fault : I didn’t see that I have to change the custom_post_id AND the taxonomy_id on line 39 in the template file… I realise a bit late that ‘types=’ was your taxonomy id and not an argument 😉

    Anyway, I wonder now :
    What code can I add to your $query to assign a “current” class to the active filter/order by value ?


    Henry from Belgium (Europe)

  16. Hunor says:

    Nice tutorial. Thanks. But how to make a field for active tags? ex. breadcrumb for tags.


  17. thirumani guhan says:

    Thank you so much…. nice tutorial….

    Very easy to understand.

Leave a Reply