Tutorial: Building WordPress-Driven Microportfolios

Tutorial Overview This is the technical follow up to the case study of my microportfolio system. It describes how to use WordPress to implement a portfolio system that will also support microportfolios, or portfolios with selected content tailored for specific audiences and purposes. This post doesn’t go into the why of the portfolio but rather […]

October 26, 2018 - Product Design, Technical Writing, UI, User Experience

Tutorial Overview

This is the technical follow up to the case study of my microportfolio system. It describes how to use WordPress to implement a portfolio system that will also support microportfolios, or portfolios with selected content tailored for specific audiences and purposes. This post doesn’t go into the why of the portfolio but rather the how so that anyone interested in stealing the microportfolio for themselves can do so.

mcleodm3.com - the comprehensive portfolio with content not tailored to a specific audience.
ecorp.mcleodm3.com - A microportfolio for the fictional Ecorp with content tailored for that job and portfolio objects selected specifically for those circumstances.

Why not a GitHub repo? I thought about it, but the way I designed this functionality is less about a unique set of code than it is a combination of information structures and options in WordPress, neither of which make for a compelling repo. Instead, this post will lay out all of the WordPress customization and cPanel modifications needed. So forking easy!


While the structure of what I’ve designed could potentially be implemented in other content management systems, this one assumes the portfolio will be run out of WordPress. The following components are necessary for building the microportfolio system:

  • A functional WordPress installation, hosted either locally or with a service provider
  • Basic knowledge of WordPress custom post types
  • The Custom Post Type plugin, installed and active
  • The Advanced Custom Fields plugin, installed and active
  • A web hosting service with access to cPanel (or some other method for working with subdomains)

Planning: custom post types

The entire purpose of a microportfolio is being able to reuse content for a variety of purposes, which means that I needed content in place to draw from before I could even think about a micro version of it. To that end, I set about building the comprehensive portfolio – the large, untargeted portfolio with all of the content – before I could do anything else.

I knew that I wanted my portfolio to include both pages and posts, the standard WordPress information objects. I would use pages for standard portfolio content, including an “about” and a “contact” page, while I’d use posts to publish my blog content. I determined that I’d need two additional information objects that aren’t normally part of WordPress:

  • “Work Samples” – this was a blanket term I settled on to describe the various content I’d include in the portfolio. I considered terms like “sample” or “example” but since I was including a variety of objects like case studies, writing samples, and conference talks, “work sample” seemed more inclusive.
  • “Microportfolios” – this would be the container object for each microportfolio, the object that would contain many work samples as well as the unique text needed to distinguish each portfolio for the requirements of the situation.
The relationship between comprenensive portfolio and microportfolios, which draw from the content of the comprehensive.

I would need to create custom post types to add “work samples” and “microportfolios” to WordPress. Long ago I would have insisted on creating these new post types by manually writing code, but the Custom Post Type plugin has gotten so good that I decided to use it instead.

Setup, Step 1: “work sample” post type

As an object, a “work sample” would not look much different from a blog post in WordPress. That’s actually the standard method for using WordPress to maintain a portfolio – using the standard blog “post” content type, but using a tag for “portfolio” pieces or some similar method for distinguishing pieces from posts.

My information architecture brain refused to settle for that. I see the two types of objects as different enough that they should be considered different types of objects in the system. In particular, I wanted to give the “case study” sample type some additional structure to make it different from smaller samples like conference talks or writing samples.

A screenshot from a case study in my portfolio - the additional metadata describing the case study was important enough to register samples as their own content type.

I first created the “work sample” post type using the Custom Post Type plugin. I created it by clicking the “Add / Edit Post Types” item under “CPT UI” and using the following settings:

Basic Settings

  • Post Type Slug: work
  • Plural Label: Work Samples
  • Singular Label: Work Sample

Settings (keep defaults for all but this)

  • Has Archive: true (makes it possible to browse samples)


  • Title
  • Editor
  • Featured Image

Built-in Taxonomies

  • Tags (WP Core)

Once the custom post type was created, I created new taxonomy called “Sample Types” that I could use to differentiate samples: Case Studies, Scholarship, Talks, Writing Samples, etc. I did this by clicking “Add / Edit Taxonomies” under “CPT UI” and using the following options:

  • Taxonomy Slug: types
  • Plural Label: Sample Types
  • Singular Label: Sample Type
  • Attach to Post Type: Work Samples

Once I had the taxonomy and I could find individual case study posts, I used the Advanced Custom Fields plugin to give additional structure to “case study” (field name, field type):

  • Client / Project – (name, text) – who the work was for
  • Timeline – (timeline, text) – when the work was done
  • Objectives – (objectives, text) – key outcomes from the work
  • Team – (team, text) – members of the team
  • My Role – (role, text) – a brief description of my role on the project
Advanced Custom Fields adds additional data points to the work samples objects that let me add additional metadata to case studies.

Additionally, when creating the custom fields, the “Location” option will tie these fields specifically to work types so that they aren’t available on other post types. Define the following rules: Show this field group if Post Typeis equal toWork Sample.

Creating the Work Samples

With a structure in place to differentiate blog posts from portfolio pieces, I was ready to start producing content in WordPress. I had already drafted several pieces in Google Docs, but now I could move my production environment to WordPress. I wanted at least four portfolio pieces in place so that I could use real content when building the next big piece – the microportfolio.

Shortcode for Including Samples

Once there are samples ready to display, I needed a custom WordPress shortcode that I could use to tell WordPress which samples to display. There’s no technical reason to do this before creating the microportfolio post type (link) where samples would normally be displayed, but I chose to do it at this point because I wanted to make sure everything I needed from the sample type was included and ready to use.

This is my custom annotation for my microportfolio. Read More

  • sample – this is the name of the function WordPress will recognize. It must be the same for each sample or WordPress will not know to execute the custom shortcode.
  • id – the id of the work sample to include. This is not the URL of the sample, but rather the id of its record in WordPress, which can be found by hovering over the link in the post list.
  • annotation – this is the text I can customize for each sample object. The annotation text will match what is included here in the shortcode call, not anything stored with the sample, so I can make it whatever I want.

To create this shortcode in WordPress, I opened the theme’s functions.php file and added the following PHP:


function sample_embed( $atts ) {

global $post;

$thispostId = $post->ID;

$thispostTitle = get_the_title($thispostId);

$a = shortcode_atts( array(

‘id’ => ”,

‘annotation’ => ”,

‘title’ => $thispostTitle,

), $atts );

$permalink = get_permalink( $a[‘id’] );

$title = get_the_title($a[‘id’]);

$post_thumbnail_id = get_post_thumbnail_id($a[‘id’]);

$image = wp_get_attachment_image_url( $post_thumbnail_id, ‘full’ );

return “<a  target=’_blank’ href='”. $permalink .”‘>

<article class=’examplebox’ style=\”background-image: url(‘”. $image .”‘)\”>

<header><h1>”. $title .”</h1></header>


</a><p>”.$a[‘annotation’].” <a href='”. $permalink .”‘>Details</a></p>”;


add_shortcode( ‘sample’, ‘sample_embed’ );

When this function is called using the shortcode demonstrated above, WordPress will output HTML:

  • A link <a> that points to the proper URL for the sample
  • An <article> element containing the sample content
    • The article has a background image matching the featured image of the sample
    • The class ”examplebox” is applied here but can be customized as needed
  • A <header> element containing the title of the sample
  • A <p> paragraph with the annotation and a link to post details

The benefit of using a shortcode to render the portfolio items is that the shortcode can be used as many times as needed with different ids and annotations, making it easy to include as many samples as I need for the specific portfolio.

With the shortcode ready to go, it was time to set up the microportfolio post type.

Setup, Step 2: the “microportfolio” post type

The “microportfolio” post type is the content people would see when they look at a custom portfolio. I refer to information structures like this as “container objects” because they literally hold all the other important content. Creating them as a new post type is straightforward because they aren’t meant to be as robust as work samples. Again, I clicked the “Add / Edit Post Types” item under “CPT UI” and used the following settings:

Basic Settings

  • Post Type Slug: portfolio
  • Plural Label: Microportfolios
  • Singular Label: Microportfolio


  • Title
  • Editor
  • Featured Image
  • Excerpt
  • Custom Fields
  • Page Attributes
  • Post Formats (necessary for choosing a new template)

Adding Customizable Text Options

Once the post type was created, I added several fields to it using the Advanced Custom Fields plugin. These fields are how I made it possible to add additional text elements to the portfolio beyond just the text of the body. Specifically, I created new fields for these nuggets of text:

  • Portfolio Tag – the introductory text following the personalized greeting
  • Portfolio Headline – custom headline that precedes the introduction (differs from the title)
  • Portfolio Text – framing, introduction to the portfolio pieces, description of why I want to work there
The custom fields I added to the microportfolio to customize the sections.

Again, as with the work sample post type, the “Location” option ties these fields specifically to microportfolio post type so that they aren’t available on other post types. Define the following rules: Show this field group if Post Typeis equal toMicroportfolio.

Prevent Default URL Behavior

The purpose of this project is to make each microsite look special, living in its own subdomain (e.g. ecorp.mcleodm3.com). However, since I set microsites up as a custom post type, their default URL is going to look like this:


Having this URL in addition to the normal URL is technically *fine*, but I don’t like it. I only want the microportolios accessible by those to whom I give the direct URL, so I want to prevent users and bots from accessing them at the standard URL.

To prevent that, I’ll make the following addition to my .htaccess file:

RewriteRule ^portfolio/(.*)$ /work [R=301,NC,L]

This redirects all virtual WordPress pages in the portfolio subdomain to the /work page that I set up to aggregate all samples. This way, even if someone guesses the correct path of the microportfolio, WordPress will direct them away.

Custom Template

With setup complete for the microportolio, all that remained was creating a template to display the portfolio content once it’s been created and saved. There is nothing particularly special about how I set up my template; it adheres to standard WordPress practice for creating customized page templates, but I did add a couple of bits to grab the custom portfolio content.

I started by creating a new file in my theme called page-custom-portfolio.php and added the following to the beginning:




Template Name: Custom Portfolio Layout

Template Post Type: portfolio


get_header(); ?>

<?php $fields = get_fields(); ?>

This is the standard way to open a new page template; the Template Name and Template Post Type are used by the WordPress UI when choosing the template you want for your post. The get_fields() function call connects to the Advanced Custom Fields plugin and loads all of the extra fields for the portfolio so that they can be used as needed in the template.

<div class=”tag”>Hello, my name is</div>

<h1>Mike McLeod</h1>

<p><?php echo $fields[‘portfolio-tag’]; ?></p>

This is the bit of markup I use in the header of the microportfolio layout. It renders the opening and my name and then accesses the custom field called portfolio-tag to output that custom field in the headline. Any of the custom fields can be output using this method, with portfolio-tag changed to the name of the other custom fields.

<div class=”opening”>

<h2><?php echo $fields[‘portfolio-headline’]; ?></h2>

<p><?php echo $fields[‘portfolio-text’]; ?></p>


Lastly, this bit forms the middle section of the microportfolio where I can directly address the circumstances of this portfolio. The portfolio-headline field is the header of that section and then portfolio-text produces the body text for that section that I’ll compose using a WYSIWYG when creating the microportfolio.

That’s it. There’s no additional customization necessary to create the microportfolio layout I’m currently using on my site. The normal the_content(); function in WordPress will see my shortcodes and render my samples without me having to do any additional labor. With that in mind, it’s time to create an actual microportfolio.

Creating a Microportfolio

The process for adding a microportfolio is almost identical to creating a regular WordPress post. Using the “Add Microportfolio” option is the standard composing space for WordPress. There are only two significant differences.

The WYSIWYG editor in the microportfolio creation form populated with shortcodes to output selected work samples.

The standard WYSIWYG composing space is where you add the sample shortcodes using the format referenced earlier and demonstrated in the screenshot. You can add as many posts as needed using this method, as long as all of them call the “sample” function, refer to a real sample ID, and have an annotation.

The custom fields as displayed in the portfolio creation form where they can be filled out like any other element.

The other distinct step in creating a microportfolio is filling out the custom fields. Much like the distinct fields for case studies, these will only be seen on microportfolios and not on any other post types. The Tag field is the only required one; failing to include either a headline or text will cause that section not to display in the template.

Once those values are entered, publish the post like any other, and the composing process for the portfolio is complete.

Create the Subdomain in Cpanel

Once the microportfolio exists, the subdomain for the portfolio has to be registered with the hosting service. Most web hosts use a platform called cPanel for managing your service, and it has a tool for managing subdomains.

The cPanel subdomain settings in my portfolio osting space.

There are two important variables to keep in mind when creating your subdomain: the actual subdomain you want (in my case, ecorp.mcleodm3.com) and the Document Root. The root is the folder on your hosting space where the index file will live (see the next section for more on that). In this example, I chose portfolio/ecorp for my subdomain – this means I’ll have one folder called “portfolio” where all of my microportfolios will live, and another folder here called “ecorp” where this specific one will go.

Upload Index File

Once the subdomain has been registered, a php file needs to be put in that space on the server that will connect with WordPress and display the portfolio. This code of the index file is pretty straightforward:


//BASED ON https://bizzybizzycreative.com/adding-a-subdomain-for-a-page-on-your-wordpress-website/




$_GET[‘post_type’] = “portfolio”;


define(‘WP_USE_THEMES’, true);



Modify this code in these ways:

  • $_GET[‘page_id’] – change the value to match the ID of the portfolio you created (hover over the name in the list view to find the ID)
  • $_GET[‘post_type’] – do not change this unless you changed the name of the custom post type for your portfolio.
  • require(‘../../public_html/wp-blog-header.php’) – this will need to change based on the configuration of your web host. This is the path on the server to the WordPress file that renders the template for the portfolio. If you’re not familiar with how to navigate the folder directory of a web host, you may need to consult your host to find that value.

Once those values have been set, upload the index.php file to the subdomain’s document root (in the case of this example, portfolio/ecorp).

Add SSL to Subdomain

At this point, the microportfolio should be visible at the subdomain. However, it’s very likely that the browser will return a nasty security error if your host does not automatically apply SSL encryption to all of your subdomains.

If this is the case, you’ll need to repeat this step for every portfolio you create. You can find free SSL certificates at resources like SSL for Free and then update the settings for the subdomain using cPanel. Installing the certificate should clear up the security warning and the microportfolio should load securely.


With the setup work complete, it should be easy now to create custom microportfolios as needed using the work samples already created. Technical writing in action!