View Demo

Whether you’re showcasing your latest and greatest work, creating a fancy team page, or you’d like a way to elegantly organise your posts – this tutorial will give you everything you need to get a lightweight, flexible, filterable set of posts.

To give you complete flexibility, we will not be using any plugins. Doing it this way reduces the chances of a future plugin update ruining your theme and prevents any conflicts with other plugins you may have.

Here’s a quick summary of what we’ll be doing today

  • Creating a custom Post Type to store our projects
  • Creating a custom Taxonomy to categorise our projects
  • Querying our Post Type and Taxonomy in our theme
  • Using Isotope to filter our projects
  • Creating our projects archive page
  • Styling everything with CSS (or SCSS)

Setting up

Firstly we’re going to need to create a place for our projects to live, and a way to categorise them so they can be filtered.

1. Create a custom Post Type

A custom post type is a core WordPress feature that allows you to organise your data into manageable sections. By default, WordPress has Posts and Pages.

We want to add a specific custom post type that will store our portfolio items inside. We will need to add some code to our theme’s functions.php

function projects_post_type() { 
  register_post_type( 'projects',
    array( 'labels' => array(
      'name' => __( 'Projects'),
      'singular_name' => __( 'Project'),
      'all_items' => __( 'All Projects'),
      'add_new' => __( 'Add New'),
      'add_new_item' => __( 'Add New Project'),
      'edit' => __( 'Edit'),
      'edit_item' => __( 'Edit Project'),
      'new_item' => __( 'New Project'),
      'view_item' => __( 'View Project'),
      'search_items' => __( 'Search Project'),
      'not_found' =>  __( 'No Projects found, yet!'),
      'not_found_in_trash' => __( 'Nothing found in Trash'),
      'parent_item_colon' => ''
      ),
      'description' => __( 'A place for our projects to live'),
      'public' => true,
      'publicly_queryable' => true,
      'exclude_from_search' => false,
      'show_ui' => true,
      'query_var' => true,
      'menu_position' => 5,
      'menu_icon'=> 'dashicons-book-alt',
      'rewrite'	=> array( 'slug' => 'projects', 'with_front' => true ),
      'has_archive' => true,
      'capability_type' => 'post',
      'hierarchical' => false,
      'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields')
    )
  );
  
}

Here, I have called our portfolio items Projects as that’s what I prefer to use – but you can use anything you like.

If you wanted to create your own custom post type you can either change the code above, or visit GenerateWP’s custom post type generator page to save yourself time.

2. Create a custom Taxonomy

Taxonomies are the Categories your pages/posts belong to. WordPress posts come with Categories and Tags by default, but we are going to create a custom Taxonomy called Project Type. This will be the heart and soul of our filtering.

Still in your functions.php below your custom post type, copy and paste this code:

function project_type_taxonomy() {

// Define the taxonomy
  $labels = array(
    'name' => _x( 'Project Type', 'Project Type' ),
    'singular_name' => _x( 'Project Type', 'Project Type' ),
    'search_items' =>  __( 'Search Project Types' ),
    'all_items' => __( 'All Project Types' ),
    'parent_item' => __( 'Parent Project Type' ),
    'parent_item_colon' => __( 'Parent Project Type:' ),
    'edit_item' => __( 'Edit Project Type' ), 
    'update_item' => __( 'Update Project Type' ),
    'add_new_item' => __( 'Add New Project Type' ),
    'new_item_name' => __( 'New Project Type Name' ),
    'menu_name' => __( 'Project Type' ),
  ); 	

// Now register the taxonomy
  register_taxonomy('project_type',array('projects'), array(
    'hierarchical' => true,
    'labels' => $labels,
    'show_ui' => true,
    'show_admin_column' => true,
    'query_var' => true,
    'rewrite' => array( 'slug' => 'project_type' ),
  ));
}

add_action( 'init', 'project_type_taxonomy', 0 );
Important: If you have changed the name of your post type, you need to change the reference on line 19 from 'projects' to your custom post type key.

3. Add some content

We will need to add some Projects and assign Project Types to them so we will be able to test out the filtering:

Remember to refresh your permalinks after adding a custom post type by going to Settings > Permalinks and pressing Save Changes at the bottom


Filtering with Isotope

Isotope is a javascript library that allows easy, dynamic filtering of content. This is the part that’s responsible for filtering your portfolio items.
Note: If you’re using Isotope for a Commercial project, please consider purchasing a commercial license.

1. Download Isotope

You can download from their website… or use a CDN like this. Move the downloaded file to your /js/ folder in your theme – if you don’t have a folder for your Javascript files to live, you can create one.

2. Add the filtering code

Create a new file in your /js/ folder and call it portfolio.js. Copy and paste the following code into the file:

(function($) {
   $(window).load(function() {

      // Tell Isotope to watch the .portfolio container
      var $container = $('.portfolio');

      $container.isotope({
         filter: '*',
         layoutMode: 'fitRows',
         resizable: false,
      });

      // When the portfolio category is clicked, filter.
      $('.portfolio-filter li').click(function() {
         var selector = $(this).attr('data-filter');
         $container.isotope({
            filter: selector,
         });
         $('.portfolio-filter li').removeClass('active');
         $(this).addClass('active');
         return false;
      });
   });

})(jQuery);

This code snippet tells Isotope that when a link is clicked in our .portfolio-filter container, filter the results in our .portfolio container.

3. Enqueue your scripts in functions.php

Back into your functions.php file again, this time we’re going to add the scripts to enqueue both Isotope and portfolio.js.

function load_scripts() {
   wp_enqueue_script( 'isotope', 'https://cdnjs.cloudflare.com/ajax/libs/jquery.isotope/3.0.3/isotope.pkgd.min.js');
   wp_enqueue_script( 'portfolio', get_stylesheet_directory_uri() . '/js/portfolio.js');
}

add_action( 'wp_enqueue_scripts', 'load_scripts' );

You may need to change the paths to your files depending on how you’ve setup your theme file structure. You’ll see I’ve decided to use a CDN for Isotope, but you can host it on your own server if you prefer.


Creating your Page Template

Here comes the fun part, we’re going to be creating a page template that will contain your portfolio. This comes in two parts.

1. Copy your page.php template and name it page-portfolio.php

Everybody will have a slightly different setup, and so the easiest way to make sure you keep all your theme styling intact is to copy your page.php template and edit the new file.

2. Tell WordPress to register your new page template

You will need to add this to the top of your newly created page-portfolio.php:

<?php
   /*
   Template Name: Portfolio
   */
?>

3. Query WordPress to show your Portfolio Items

We need to ask WordPress to show us only our portfolio items.

<div class="portfolio">
    <?php
        if ( get_query_var( 'paged' ) ) :
            $paged = get_query_var( 'paged' );
        elseif ( get_query_var( 'page' ) ) :
            $paged = get_query_var( 'page' );
        else :
            $paged = -1;
        endif;
    
        $args = array(
            'post_type'      => 'jetpack-portfolio',
            'paged'          => $paged,
        );

        $project_query = new WP_Query ( $args );

        if ( post_type_exists( 'jetpack-portfolio' ) && $project_query -> have_posts() ) :
            while ( $project_query -> have_posts() ) : $project_query -> the_post();
                
                get_template_part( 'content', 'portfolio' ); // Your portfolio content goes here.
                
            endwhile;
            wp_reset_postdata();
        else : ?>
            <p>Whoops! No portfolio items.</p>
    <?php endif; ?>
</div>

Here, we’re using WP_Query to query our projects custom post type.

If you have called your custom post type something other than projects, you will need to query your own post type key.

4. Add the structure for your portfolio items

Create a blank file in the base of your theme directory and call it content-portfolio.php. This is going to be where we create the structure for your portfolio items.

Add this code into content-portfolio.php

<?php
/**
 * This is where you create the structure for your individual portfolio items.
 */

    // Get the taxonomy terms for Portfolio custom post type.
    $terms = get_the_terms( $post->ID, 'jetpack-portfolio-type' );

    if ( $terms && ! is_wp_error( $terms ) ) :

    $filtering_links = array();

    foreach ( $terms as $term ) {
        $filtering_links[] = $term->slug;
    }

    $filtering = join( " ", $filtering_links );
?>


    <article id="post-<?php the_ID(); ?>" <?php post_class( $filtering ); ?>>
        <a class="portfolio-item" href="<?php the_permalink(); ?>" rel="bookmark" class="image-link" tabindex="-1">
                <?php if ( has_post_thumbnail() ) {
                    the_post_thumbnail();
                } ?>
                <?php the_title(); ?>
        </a>
    </article><!-- #post-## -->
<?php endif;

You can edit the HTML in this to suit your needs. You could also add a few nice effects and a logo for each project using Advanced Custom Fields.

We have now created all the elements we need to be able to display our portfolio items on our newly created page template. You’ll now need to create your Portfolio page from the WordPress Dashboard if you haven’t already, and then remember to set the page template to Portfolio!


Make it pretty

My favourite part of any build – Add some CSS to make it yours!

View Demo