Create a WordPress Product Catalog without Ecommerce, Using Custom Post Types

Posted on in Blog

Note: Oneupweb initially wrote this post about WordPress 3.0. Then, in 2020, we added information and a new conclusion to address modern-day practices. Long story short, you’ll likely want to explore using a plugin for this process today.  

What can’t WordPress do these days? The addition of custom post types made it possible to accomplish nearly endless tasks. Today we will be looking at a simple way to create products with WordPress custom types and sell them with PayPal “Buy Now” buttons. Essentially, it’s a WordPress product catalog without ecommerce solutions.

Looking to hire a website developer to help with jobs?

What we will be creating:

How it’s done:

We will leverage a custom post type (available since WordPress 3.0) and custom meta boxes to create the administrative interface for adding products. Our first step will be to create the custom product type.

Step One: Create the custom post type

At Oneupweb, we generally organize our code into Controllers, Models, and Partials. Controllers handle our business logic, object creation, and assigning of filters or actions. Models will grab any data we need to use in our Controllers, and the view is handled by Partials. You may have heard the term MVC, and that’s exactly what this is.

If you don’t feel comfortable restructuring the folders in your WordPress theme to create your WordPress product catalog template, you can absolutely dump this code into the functions.php file.

 * Let's add the custom post type

add_action('init', 'catalog_init');
function catalog_init()
  $labels = array(
    'name' => _x('Products', 'post type general name'),
    'singular_name' => _x('Product', 'post type singular name'),
    'add_new' => _x('Add New', 'product'),
    'add_new_item' => __('Add New Product'),
    'edit_item' => __('Edit Product'),
    'new_item' => __('New Product'),
    'view_item' => __('View Product'),
    'search_items' => __('Search Products'),
    'not_found' =>  __('No products found'),
    'not_found_in_trash' => __('No products found in Trash'),
    'parent_item_colon' => '',
    'menu_name' => 'Products'

  $args = array(
    'labels' => $labels,
    'public' => true,
    'publicly_queryable' => true,
    'show_ui' => true,
    'show_in_menu' => true,
    'query_var' => true,
    'rewrite' => true,
    'capability_type' => 'post',
    'has_archive' => true,
    'hierarchical' => false,
    'menu_position' => null,
    'menu_icon' => get_bloginfo('template_url') . '/images/producticon.png',
    'supports' => array('title','editor','thumbnail','excerpt')

We hook into the WordPress init action to create the product post type. Notice the register_post_type function accepts the name of the post type and the arguments array. Things to note in the argument array are the menu_icon and labels.

Step 2: Create meta boxes for product information

We want people to be able to pay for these products, so we better create a way for them to do that. We will leverage custom meta boxes for our product post type to get this done in a snap.
Add the following code to the functions.php file:

 * The custom meta boxes to handle paypal button and product price
add_action('add_meta_boxes', 'catalog_add_custom_box');
function catalog_add_custom_box() {
    add_meta_box('catalog_priceid', 'Product Price', 'catalog_price_box', 'product','side');
    add_meta_box('catalog_paypalid', 'PayPal Code', 'catalog_paypal_box', 'product','side');

function catalog_price_box() {
    $price = 0;
    if ( isset($_REQUEST['post']) ) {
        $price = get_post_meta((int)$_REQUEST['post'],'catalog_product_price',true);
        $price = (float) $price;


function catalog_paypal_box() {
    $code = '';
    if ( isset($_REQUEST['post']) ) {
        $code = get_post_meta((int)$_REQUEST['post'],'catalog_product_paypal',true);
        $code = stripslashes($code);

 <?php }

We hook into the add_meta_boxes action here and use the add_meta_box functions to do our dirty work. As you can see this function accepts the type of post this box will be added to (in our case this is product) and a callback function that handles the display of the meta box controls.

Step 3: Save the product info

Saving the product info is as easy as hooking into the save_post action.

function catalog_save_meta($postID) {
    if ( is_admin() ) {
        if ( isset($_POST['catalog_product_paypal']) ) {
        if ( isset($_POST['catalog_product_price']) ) {

Notice we check that our form values have been submitted before we update our post meta. If you don’t do this you will notice some funky stuff happen as the save_post action can be triggered at different times.

Step 4: Display the product(s)

It is simple to display one of our new products: Let’s just query our custom post type and have some good ol’ fashioned WordPress loop fun. This needs to go inside of a template file. Lets create a single template for our product type. Create a file single-product.php in your theme directory and put the following inside it:

<?php get_header(); ?>
<?php query_posts(array('post_type' => 'product')); ?>
<?php while(have_posts()): the_post(); ?>

<?php the_title(); ?>

<?php the_post_thumbnail(); ?>
<?php echo get_post_meta(get_the_ID(),’catalog_product_price’,true); ?>
<?php the_content() ?>
<?php echo get_post_meta(get_the_ID(),’catalog_product_paypal’,true); ?>
<?php endwhile; ?>
<?php get_footer(); ?>

And… shazam! A similar loop would work in any template file. You can have templates to display a single product or a catalog of them. Experiment with the possibilities!

The “2020 and Beyond” Edit

With newer versions of WordPress (we’re way past 3.0 now), it’s important to note that using the query_posts() function can adversely affect site performance. Since we are creating a Custom Post Type, the more WordPress-y solution would be to create an Archive Template that matches our Custom Post Type. In this case it would be “archive-product.php”. You can see the Template Hierarchy laid out in WordPress Documentation.

In addition to using an Archive Template to lay out all the Custom Post Types, we can also use a Single Template for the individual Custom Post Types using something like “single-product.php,” as you can see in the WordPress documentation for Post Type Templates.

Are Plugins a Better Solution?

When we updated this post in 2020, WordPress had reached version 5.5. Now you can leverage plugins to do all the ecommerce-related heavy lifting for you. And we recommend that you do so.

WooCommerce is one of the biggest names in the game, and this is for good reason. Out of the box, this ecommerce solution can handle almost everything you could possibly need to sell products on your WordPress site. If you mix that with the help of Yoast SEO, you’ll have the included Schema Markup to boot! Learn more in our guide to creating an ecommerce website.

Our developers still use Custom Post Types often but generally not for an ecommerce solution. We use Custom Post Types for things like Careers, Locations, Team Members, and any other group of posts we’d like to have more frequent and custom control over.

If you’re looking for development, SEO and other support for your ecommerce website, Oneupweb has you covered in any department. Reach out to us here or call (231) 922-9977.

Up Next

There’s no doubt video marketing is a proven winner. Marketing teams in every industry know that creating the right video content for the right platform can substantially improve brand awareness and drive conversions. But with limited time, budget and resources, most marketers have to prioritize content creation. If you’re choosing between a robust TikTok presence...

Read More