How to build CRM object dynamic pages

Last updated:

Dynamic pages for CRM objects are in beta. This feature requires your account to be ungated. To request access to this beta, you must sign up. By signing up you agree to our developer beta terms and conditions. As this is a beta, functionality may change. 

A dynamic page is a CMS page that gets its content from a structured data source like HubDB or CRM Objects. Dynamic pages have a varying URL path suffix that "dynamically" controls the content displayed in the page. Dynamic pages typically use a template that implements a "listing view" and an "instance view". The listing view displays a collection of items and the instance view displays the details of each item.

Custom objects allow you to store any type of data in HubSpot—particularly data that doesn't fit the standard CRM objects. Custom objects are created via the custom object endpoints and can be associated with standard objects.

With CRM object dynamic pages, a page is created for every instance in your object type. Each dynamic page includes its own unique, SEO-friendly URL, and offers page-specific analytics. The properties of your custom object can then be displayed within your dynamic pages.

If you've used HubDB to generate dynamic pages this will feel similar. The primary difference is that CRM objects are a data source in your CRM. This means you can more easily associate objects and contacts. This can be a powerful tool for marketing and sales, because it has benefits throughout the entire HubSpot platform, enabling workflows, reporting etc. 

You’ll need:

  • Some prior knowledge of HubSpot's CMS, HTML and CSS will be needed to customize your page
  • A custom object or other CRM object such as a product or marketing_event
  • Data stored to that CRM object

Additionally if using custom objects you'll require CMS Hub Enterprise, or Marketing Hub Enterprise with CMS Hub Pro.

This guide and the API guides use the example of a cars custom object, which would store data on individual cars at a dealership. This guide is written so you should be able to apply this to your own use case just substitute cars for the collection of objects and car for the single object instance.

1. Define or update your custom object schema

If using the products or marketing_event standard objects you can skip step 1, and go straight to step 2.

Dynamic pages have special fields, such as dynamic page slug, page title, meta description, and featured image. The dynamic page slug is required for dynamic pages to be generated. In a later step, you will map your custom object's  properties to these dynamic page fields.

Dynamic page properties
Dynamic page propertyfieldtypeDescription
Dynamic page slug
Required
text

URL slug that your detail page will display at for the individual object instance. The full URL will include the CMS page's URL with the slug appended. The property you choose needs to be a text with hasUniqueValue. Additionally consider the length of the property value and how human readable it is. How much either length and human readability matters depends on your use-case.

Since this property will be used for a URL, it needs to be URL friendly.

Needs to be "HasUniqueValue":"true".

Page Title
text

This ideally should also be unique, and should be assigned to a property that succinctly describes the individual object instance.

This should uniquely identify the content of the page to stand out in search results.

Meta description
text

This should be a simple description of the individual object instance, it will affect the page's SEO.

This should uniquely identify the content of the page to stand out in search results.

Featured image
text

If you have an image property associated with your object, it would make sense to display this.

You don't need to literally have fields with these exact names in your custom object definition.

When building a cars custom object we could use a Vehicle Identification Number (VIN) as a page slug, since we know every car's VIN is unique.

If you don't have a custom object defined already: follow the instructions to define a custom object schema using the API, then come back here for step 2.

If you do have a custom object defined but don't have properties to map the dynamic page fields to, use the API to update the object schema. Add any of the dynamic page fields as properties if existing properties won't do.

2. Create custom object instances

 You can create new CRM object instances, and view your CRM object instances in a table, as well as filter by property values. You will need the custom objects access permission.

There are two ways to create CRM object instances, choose the method that you are most comfortable with:

Once you have some object instances move on to the next step.

3. Create a module

For dynamic pages to function, you don't need a special template, but you do need to access the CRM object information from the page settings. Our recommendation is to create a custom module to do this. This module will display both the listing data and the dynamic individual object instance page data.

Create a new custom module. In the module.html add

HubL
{% if dynamic_page_crm_object %}{#detail page#}
   {% set car = dynamic_page_crm_object %} {# easy variable to access the CRM objects properties from #}
   {# To see all of the properties your object during development, print it to the page with the |pprint filter #}   
   <h1>{{car.year}} {{car.make}} {{model}} </h1>
    <div class="car">
       <div class="carImage">
           {% if car.image %}
           <img src="{{car.image}}" alt="{{car.year}} {{car.make}} {{car.model}}">
           {% else %}
           <img src="https://f.hubspotusercontent20.net/hubfs/9307273/Imported%20images/plchldr255.png" alt="Picture coming soon">
           {% endif %}
       </div>
       <div class="carDetails">
           <div class="carPrice">
               Price: {{car.price |format_currency("en-US")}}
           </div>
           <div class="carBody">
               Body Type: {{ car.body_style.name}}
           </div>
           <div class="carDescription">
               {{car.description }}
           </div>
           <div class="carDistance">
               Distance from Cambridge: <span class="dealer_location"> {{car.location  }}</span> Miles
           </div>
           <div class="carListingDate">
               Time since listing: <span class="listing_date">{{car.date_received}}</span>
           </div>
       </div>
   </div>
 
 
{% elif dynamic_page_crm_object_type_fqn %}{# listing page #}
   {# {% set properties_list = content.properties_to_display_in_dynamic_page|join(', ') %} #} {# "Properties to fetch" can be pulled from page settings #}
   {# If you know there is a specific set of properties that you need to display pass a string with comma separated property names #}
   {% set properties_list = "vin, price, picture, location, body_type, date_received, image" %}
   {% set cars = crm_objects(dynamic_page_crm_object_type_fqn, "limit=200", properties_list) %}
 
   <div class="carListing">
       {% for car in cars.results %}
           <div class="carCard">
               <a href="{{ request.path }}/{{ car.vin }}">
                   <div class="carImage">
                       {% if car.image %}
                       <img src="{{car.image}}" alt="{{car.year}} {{car.make}} {{car.model}}">
                       {% else %}
                       <img src="https://f.hubspotusercontent20.net/hubfs/9307273/Imported%20images/plchldr255.png" alt="Picture coming soon">
                       {% endif %}
                   </div>
                   <div class="carDetails">
                       <div class="carPrice">
                           Price: {{car.price |format_currency("en-US")}}
                       </div>
                       <div class="carBody">
                           Body Type: {{ car.body_type}}
                       </div>
                       <div class="carDistance">
                           Distance from Cambridge: <span class="dealer_location"> {{car.location}}</span>
                       </div>
                       <div class="carListingDate">
                           Time since listing: <span class="listing_date"> {{car.date_received }}  </span>
                       </div>
                   </div>
               </a>
           </div>
       {% endfor %}
   </div>
{% endif %}

In this code we are checking if the page is a CRM object dynamic page or a CRM object listing page. If it's a dynamic page, our individual car data is available in the page.

If it's a listing page, we use the CRM objects function to query for the object type. content.properties_to_display_in_dynamic_page is an array. That array is populated by the properties to fetch field when editing a dynamic page's settings.

Change the variable names and HTML in the module.html for your specific use-case.

4. Add your module to a page

Create a new page for your dynamic page listing. Choose a page template that has a drag and drop area or flexible column. This page will also store the configuration for your detail pages.

Using the "Edit Modules" panel, add your module to the page.

Now open "the Page Settings" tab. 

Scroll down to "Dynamic Pages".

Screenshot of the page editor's page settings screen. It's cropped to only show the data source, and dynamic page slug fields for dynamic pages.

Set "Data source" to your custom object.

The "Dynamic page slug" needs to be unique. In our cars example a VIN is always unique, making it an excellent dynamic page slug.

Map your object's remaining properties to the appropriate fields. The featured image, could for example be a photo of the car.

Set your "properties to fetch" to properties from your object, needed to display on your page.

Screenshot of page settings showing the properties to fetch field. The field has the values VIN, Price, Image, Location, Body Type, and Date received entered.

When ready, publish your page.

The page URL for this page will be the base URL for all of the dynamically generated detail pages.

In our car example we would set the slug to: /cars

The dynamically generated detail pages will display at: /cars/VT123234345

The second part of the path being the property we selected as the dynamic page slug. In our example we chose the VIN.