Why You Should Add a List of Projects You’ve Built To Your Blog

I’m going to pitch you an idea: if you have a personal blog, you should create a Projects listing page.

Beyond the regular blog post pages and listing pages, many sites also have an About page.

Over the years many bloggers have added /now pages, popularized by Derek Sivers at

Recently there’s been a spike of bloggers listing the apps they use, popularized by the Hemispheric Views podcast episode. As of this writing, there are already 330 blog posts of peoples apps. Not bad for two months!

One other page I personally love to see is a Goals page. Any time I find one I devour it and update my own with new dreams.

While Goals look forward, I liked the idea of creating something that looked back. For that I decided to create a Projects page.

What is a Projects Page?

A Projects page is where you list all projects you’ve worked on in your area of expertise. If you’re a programmer this could be all programming projects. If you’re a woodworker, this could be all of your creations. If you’re an artist, this could be your body of work.

The important part is to include everything. Your successes, your failures, your works in progress, projects that have been retired, projects that never launched and anything else you can think of that you did enough work to consider it significant.

What you consider significant will be subjective. There are projects I prototyped out and wrote a few lines of code but never made significant progress on that I added because I spent a lot of time on them. Only you will know what’s significant.

What you decide to show, and how you want to show it, is up to you. This could be a single new post you create with a full list that you update. It could be a folder of Markdown files or a new addition to your CMS (more on how I did mine later). In my case, I added all of them to WordPress as a new Post Type (Project) and list them all on my Projects Page.

A Celebration of Your Success

A lot of us don’t get to celebrate our successes enough. It can come off as bombastic or bragging if taken to extremes and that can sometimes prevent us from taking that time to enjoy what we’ve done.

For me, creating this Project listing was rewarding. It was a celebration of projects I completely forgot about, yet thinking of them made me smile.

Some of these are weekend creations like NG Guess, an Angular app where people could when the new version of Angular would come up. Many (17) are Code Schools courses I worked on in various roles. Some projects never launched, liked Seek Adventures, an site that would turn AllTrails hikes into a beautiful interactive map.

Some of my favorites are the earliest websites I ever made. A Dance Dance Revolution community. A Settlers of Catan league. A marketplace that showed the in-game cost over-time for digital items in the game Ragnarok Online. An fan site for an anime IRC Channel.

As of today I just added my 100th project to this page, The Hardcover 2023 Year in Books! 🥳

Some projects never launched. The most were seen by 10s of millions of people (the free Code School courses for Try jQuery and Angular were linked from the homepages of those frameworks, making them the most widely viewed projects I’ve ever worked on).

I absolutely love seeing other peoples Projects, and I’d love to see yours! If you’ve created one comment on this post with the link

Technical Breakdown of My Projects Page

In late early 2023 I migrated this blog from using Middleman, a Ruby static site generator, to using Next.js backed by a headless WordPress CMS. Like a lot of indie creators, I’ve used this site to experiment with new technology over the years.

The most significant change when switching from Middleman to headless WordPress is where data is stored. Rather than having posts in Markdown files, they’re now stored in a database that I can update from a website.

That worked well for posts, but less well for other content. When I started my Projects page, it didn’t cross my mind to put that content in WordPress. It was structured completely different and didn’t match up with a traditional post.

A few weeks ago, while prototyping an idea I couldn’t get out of my mind, I found a solution I didn’t even know I needed.

Advanced Custom Fields

WordPress has always has had an option for “custom fields” for as long as I can remember, but it always felt hacky – until I found Advanced Custom Fields.

Advanced Custom Fields, or ACF for short, allows for two incredibly useful additions to WordPress.

First, you can create new Post Types. A Post type is a high-level concept of a post that you want to have multiple of. For my Projects page, I created a new post type of Project. On Hardcover, we recently created a new type of “Live”, which we’re using for Hardcover Live episodes.

Second, you can create groupings of custom fields which are attached to a post type. For Hardcover Live, we created an “Episode Info” group with fields for Episode Number, YouTube ID, Podcast URL and more. For Projects I used this same approach to set links, dates and more. My favorite part was being able to upload an icon from the WordPress admin.

ACF Settings for a Project

With this group created, it can be attached to posts of a specific type – in this case Projects.

With that combination in place, each Project will include a form at the bottom of it with each of those fields.

Some of these fields are freeform, others are drop downs with a predefined list of options.

For technologies, I used WordPress tags. That also means if I tweak any single tag in one place it updates them everywhere.

Free ACF Limitations

All of this can be done completely for free. Advanced Custom Fields Pro starts at $49/year, a little much for this blog. Fortunately everything I needed for the Projects page was available for free.

There is one thing that isn’t: repeating fields. For Projects, sometimes I wanted to add a dynamic number of links. ACF Pro has a solution for this: Repeater fields. Repeater fields allow you to create a group and then, well, repeat them.

Instead of paying the extra cash, I added a bunch of optional fields for “link 1”, “link 1 text”, “link 2”, “link 2 text” — all the way up to link 5.

It’s hacky, but it’s free.

GraphQL ACF Access

ACF gets the data into WordPress, but you’ll still need a way to get it out. To my amazement there’s a plugin that does everything we need: WPGraphQL for Advanced Custom Fields.

With this plugin installed, you get a brand new option for each section.

ACF Section option added by WPGraphQL for Advanced Custom Fields

Now, you’d think this would be everything you’d need to do. However, this only works if the Post type is a Post. In this case the post type is a Project. Fortunately, there’s a quick fix. We need to register this post type in GraphQL in our theme file.

They way I prefer to do this is updating the functions.php file for my theme and adding the following line:

// Require all custom code
require get_template_directory() . '/../custom.php';Code language: JavaScript (javascript)

Then create a file at wp-content/themes/custom.php. By splitting this code out into a non-theme specific file it’ll be a lot easier to change the theme later on. This file registers projects in GraphQL

// Show Tools in GraphQL
add_filter( 'register_post_type_args', function( $args, $post_type ) {

  // Change this to the post type you are adding support for
  if ( 'project' === $post_type ) {
    $args['show_in_graphql'] = true;
    $args['graphql_single_name'] = 'project';
    $args['graphql_plural_name'] = 'projects'; # Don't set, and it will default to `all${graphql_single_name}`, i.e. `allDocument`.

  return $args;

}, 10, 2 );Code language: PHP (php)

I’m no expert in WordPress, so finding this took some trial and error. I knew it was working when the GraphiQL IDE in WordPress showed these new fields.

With that, the data was now available!

Accessing ACF Data from Next.js

If you’ve already setup Next.js to pull down posts, then pulling down Projects will be a breeze.

import { getClientForProject, parseProject } from '@/lib/wordpressClient'
import { Project } from '@/types'

export const findProjects = `
query GetProjects {
  projects(first: 10000) {
    nodes {
      projectInfo: project {
        icon {
          mediaDetails {
      excerpt(format: RAW)
      tags {
        nodes {

export const getProjects = async (): Promise<Project[]> => {
  const result = await getClientForProject('adamfortuna')({
    query: findProjects,

  return any) => parseProject(node))
}Code language: TypeScript (typescript)

The parseProject method is doing some heavy lifting of converting the WordPress format into a Project type that my front-end relies on.

React.js & User Experience

The actual front-end is standard React. In fact, it’s probably bad React. Sorting happens entirely on the front-end, there are some flickers, it’s the minimum needed to work.

It’s the kind of code you’d expect – looping over projects and showing them. Nothing fancy.

The “Organize By” part sets state a level higher and toggles which components and fields are used to render the projects. That combination of different listing components + sort field allows for a ton of flexibility. Start date, end date and technology (WordPress tags) were the most useful to me.

Other Blogs Projects Pages

Have you created a project page? Let me know in the comments or on Mastodon and I’ll add a link to it here! I’d love to see a bunch of peoples Projects pages.

  1. Adam Fortuna’s Projects
  2. Your project here.
Avatar for Adam Fortuna

Hey hey! 👋

I'm , a full-stack product developer in Salt Lake City, UT. I love enlivening experiences, visualizing data, and making playful websites.

Let's keep in touch 🧑‍🤝‍🧑

Did you link to this article? Add it here