Custom Post Types

Implementing Custom Post Types and Custom Fields with Hubspot

We recently worked with a client to create a Related Products Widget on their Hubspot blog. The requirements called for a dynamic display of products that would be managed in one place, and for each product listing to have an image, url, title, short description, and price. As Hubspot is meant to be a blogging platform, its capabilities are a bit limited in terms of a CMS. It’s possible to add a variety of custom fields to a hubspot post template, but the ability to store custom sets of data within your hubspot blog does not exist, or at least not without some ingenuity.

After a bit of research and some creative problem solving, we were able to successfully create a Custom Post Type for our client’s blog on Hubspot. This guide will help you push the limits of Hubspot’s capabilities and create something unique.

Code

As I started to ponder the concept of custom post types in Hubspot, I thought, “Plenty of organizations rely entirely on Hubspot to power their website, surely someone has figured out a way to store subsets of data for their blog.”

Turns out that I was right. I soon found a great article on the Hubspot Designers Blog on how they were able to Create a Portfolio with Hubspot COS. The Hubspot team was able to display a grid of Portfolio lists on their main blog template by pulling posts from a separate blog that they created solely to store their Portfolio items.

Creating a Separate Hubspot Blog for a Custom Post Type

Using the Hubspot Portfolio article as a reference, create a separate blog for Products. As the Product Blog is only used to store content for the main blog, you don’t want people or search engines to accidentally stumble across the Product Blog. There are a variety of ways to hide hubspot content from search engines. I opted to include the no-index meta-tag within the header referenced by the Product Blog via the Blog Settings page, but you can find a number of different options for hiding content here.

HubSpot

In terms of the actual template markup for the Product Blog, copy over the basic templates from the main blog as a starting point. Feel free to go with a blank template and simply add in your custom fields as either visible or non-visible items, if you’re also creating a blog that is not meant to be visible to others.

Creating Custom Fields for Hubspot Blog Posts

Creating custom fields for a product listing is straight-forward: use the standard fields for the post title (name) and featured image, and then add in custom fields for any  extra Product Meta (we added three):

HubSpot Edit Post Template

Here is the markup that we added to the the Post Template:

{{ content.name }}
{{ content.featured_image }}
{% text "product_url" label="Product URL" %}
{% text "product_price" label="Product Price" %}
{% text "product_description" label="Product Description" %}

You can nest the  above fields within HTML tags, but for our purposes we weren’t concerned with presentation on our hidden blog. When adding a new Product Post, this is what the interface looks like:

HubSpot Product Fields

Side note: if you’re implementing custom fields on a publicly visible blog, you can always keep the fields hidden on the front end by adding export_to_template_context=True to the your fields. Example:

{% text "product_description" label="Product Description" export_to_template_content=True %}

Pulling Related Content from a Separate Hubspot Blog with a Custom HubL Markup Widget

Now that we have devised a way to add product posts on our hidden Product Blog, we need to decide how to display the product content on our main blog. The products need to be displayed based on their relevance to the blog post at hand, which can be done by leveraging Tags (or Topic List items as Hubspot calls themon the product Post, a custom field on the main blog post template, and the HubL related posts function. For an intro to this concept, I’d recommend reading about Creating a Related Posts widget with HubL markup.

Enable the Related Products to display by adding a custom HubL markup widget on the main blog post layout. This widget will hold all of the markup needed to display Related Products on the main blog posts.

HubSpot Related Products Widgets
HubSpot Related Products Markup

Here’s the key function that we need to reference for pulling our Related Products into the main blog post template:

{{ blog_recent_topic_posts('blog identifier', 'topic slug', count ) }}

As shown, three parameters are accepted:

  • Blog Identifier: This is either “default” or the numerical ID of the blog that you want to pull content from. In our case, we’re going to use the ID of our product blog.
  • Topic slug: The slug value of the topic list item or tag to display. This is all lowercase, and spaces must be replaced with dashes.
  • Count: number of posts to display

To create our related posts widget, we need to first find the ID of the Products Blog for the first parameter. This can be found by going to the dashboard for the products blog, and picking out the second number string in the url.

Blog URL

Next, we want to determine a topic slug to pull the Product Posts with. The Hubspot documentation suggests iterating through the sequence of Topic List items/tags on the blog post and using the topic list item in the sequence to pull the related posts. In that case, you would use the first tag added to the blog post.

This worked great during initial testing, but I found that Hubspot reorganizes these topic lists alphabetically on save when you have multiple tags. This is  problematic. To get around this issue add a hidden field to the main blog’s post listing template that is used to specify a slug for Related Products. Here is the markup for creating this field:

{% text "related_products_tag" label="Related Products Tag", export_to_template_context=True %}

HubSpot Related Products Tag

HubSpot Edit Tag

Next, take this related_products_tag in the widget and assign it to a variable to be used in the post loop if a value exists. To make the related_products_tag field slightly forgiving, use some HubL filters to replace spaces with dashes and to transform the entry to lowercase letters.

{% if "related_products_tag" %}
{% set products_tag = content.widgets.related_products_tag.body.value %} 
{% set products_tag = products_tag.replace(' ', '-') %}
{% set products_tag = products_tag|lower %}

Note that we’re using content.widgets to access the custom field data within our Post Template. Now let’s create our loop using the recent topics function:

{% set related_products = blog_recent_topic_posts('555555', products_tag, 5 ) %}
{% if related_products|length > 0 %}

Set the product_tag above (the 555555 number is a bogus Blog ID that I’ve included here for demo purposes). The blog_recent_topic_posts loops through the posts on the Product Blog, checks for posts that have a topic list item based on the products_tag field entry post, and then if posts exist, you can display content from the product posts. Let’s build out our product listing:

<h3>Related Products</h3>
<ul class="ProductsList">
  {% for post in related_products %}
  <li class="ProductsList-item">
    <div class="IconBlock IconBlock--alignMiddle">
      <div class="IconBlock-left">
        <div class="Product-image">
          <a href="{{ post.widgets.product_url.body.value }}" title="{{ post.name }}"><img src="{{ post.featured_image }}" alt="{{ post.name }}"></a>
        </div>
      </div>
      <div class="IconBlock-content">
        <div class="Product-name"><a href="{{ post.widgets.product_url.body.value }}" title="{{ post.name }}">{{ post.name }}</a></div>
        <div class="Product-price">
          {{ post.widgets.product_price.body.value }}
        </div>
         <div class="Product-description">{{ post.widgets.product_description.body.value }}
         </div>
      </div>
    </div>
  </li>
{% endfor %}
</ul>

In the above markup we’ve used post.widgets to access each of the custom fields on the Product Post. Each custom field returns an array of objects with various data points, all of which can be echo’d onto the screen at once by using the pprint HubL filter. Example: {{ this_var|pprint }}. In this case, we want the value of the body content for each of the custom fields.

Now we’re ready to tie it all together in our Related Products HubL widget. Here is the final Related Products Widget markup:

{% text "related_products_tag" label="Related Products Tag", export_to_template_context=True %}
{% if "related_products_tag" %}
  {% set products_tag = content.widgets.related_products_tag.body.value %} 
  {% set products_tag = products_tag.replace(' ', '-') %}
  {% set products_tag = products_tag|lower %}
  {% set related_products = blog_recent_topic_posts('555555', products_tag, 5 ) %}
  {% if related_products|length > 0 %}   
    <div class="Widget Widget--products">
      <h3>Related Products</h3>
      <ul class="ProductsList">
        {% for post in related_products %}
        <li class="ProductList-item">
          <div class="IconBlock IconBlock--alignMiddle">
            <div class="IconBlock-left">
              <div class="Product-image">
                <a href="{{ post.widgets.product_url.body.value }}" title="{{ post.name }}"><img src="{{ post.featured_image }}" alt="{{ post.name }}"></a>
              </div>
            </div>
            <div class="IconBlock-content">
              <div class="Product-name"><a href="{{ post.widgets.product_url.body.value }}" title="{{ post.name }}">{{ post.name }}</a></div>
              <div class="Product-price">
                {{ post.widgets.product_price.body.value }}
              </div>
               <div class="Product-description">{{ post.widgets.product_description.body.value }}
               </div>
            </div>
          </div>
        </li>
      {% endfor %}
      </ul>
    </div>
  {% endif %}
{% endif %}

And here’s an Example screenshot of the output:

HubSpot Related Products Final

All in all, not too difficult – proving that with a little bit of creativity and determination you can push the limits of Hubspot to create custom post types for your blog.

For more info and guidance on building out your own custom post types, check out our references: