How to build dynamic pages using CRM objects

Last updated:

Dynamic pages are CMS pages that get their content from a structured data source, such as  HubDB or CRM objects. With CRM object dynamic pages, a page is created for each instance of the object type. Each page is built on a unique, SEO-friendly URL, and displays information based on object properties.For example, a car dealership could store their inventory in a custom Car object. Then, using CRM dynamic pages, they could list their inventory online with a unique page automatically created for each car. When a new record is created under the Car object, a new page will be created automatically, keeping the inventory and website in sync.

Dynamic pages can be built using either a HubSpot standard object, such as Products, or a custom object (Enterprise only). Dynamic pages have a varying URL path suffix that dynamically controls the content displayed on 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. 

This is similar to using HubDB to generate dynamic pages. 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 is also a powerful tool for marketing and sales, because the data can be used across HubSpot’s tools, such as workflows and reporting.

Prerequisites

 

Building a CRM object dynamic page involves three steps:

  1. Prepare your CRM object with data you’ll want to display.
  2. Create a custom module to display that data, then add the module to a page.
  3. Create a listing module to display all object records on a listing page.

This tutorial will walk through how to build a CRM object dynamic page using the example of a Car custom object, which stores data on individual cars at a dealership. To adapt the code examples to your own use case, substitute Car with your own custom object, and car for the individual object records. 

Prepare your CRM object

Dynamic pages have special fields, such as dynamic page slug, page title, meta description, and featured image, which define each website page. The dynamic page slug is required for dynamic pages to be generated, and you’ll map object properties to each dynamic page field.

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. All lowercase characters, and no spaces or special characters except -.

Needs to be "hasUniqueValue":true. You can set this property using the create schema endpoint. You can't update an existing property to have "hasUniqueValue":true it needs to be created that way.

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

This is the featured image in the meta information for the page. When the page is shared this image will be used.

It is not required to have properties with these exact names, and you can use existing properties as dynamic page fields.

If you’ve defined a custom object but don't have properties to map the dynamic page fields to, use the CRM Object Schema PATCH API to update the object schema. You can use existing properties for the dynamic page fields, or create new properties. 

In our example, we’ll be using a custom property VIN (Vehicle Identification Number) as a page slug, since we know each car’s VIN is unique.

Create a module to display details for a single record

This module will display the individual object record data. In our example, the module will display detailed information about a specific car.

To set up your module:
Create a new custom module. Within this module we will use the dynamic_page_crm_object variable to access the data stored to the current dynamic page's object instance.

Tip: Naming the module something like "[object type] - detail" can make it clear what this module does. In our cars example we would name it "Car - detail".

In the module.html, add the following code:

HubL
{% if dynamic_page_crm_object and !module.car %}{# detail page and content creator has not selected an object#}
 
   {% 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>
 
{% else %}{# The page is not a dynamic page or the user selects a specific object, we can get the data from a CRM object field if there is data. #}
 
  {% set car = module.car.properties %}
  {# 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 #}   
 
  <section aria-label="featured 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 %}
        <h3 class="car-name">{{car.year}} {{car.make}} {{model}} </h3>
        <div class="price">
          {{car.price|format_currency("en-US")}}
        </div>
      </div>
  </section>
{% endif %}

This code checks if the page is a CRM object dynamic page. If it is a CRM object dynamic page, use dynamic_page_crm_object to retrieve the object record data. Object record data will be retrieved based on the object type set in the CRM object field.

This makes the module useful outside of the context of a dynamic page as well, as you can use it to feature object records on dynamic pages by having multiple instances of the module.

In the above code, we’ve created a CRM object field of car. To fit your use case, you can update the module.car references in the code to your field’s name.

After creating your module, you’ll then add the module to a page.

Add your module to a page

  1. Create a new page for your dynamic page listing. Choose a page template that has a drag and drop area or flexible column. 
  2. In the page editor, click the "Settings" tab.
  3. Set your "Page URL" to the URL where you want your listing page to appear.
  4. Open "advanced options".
  5. Under Dynamic Pages, click the Data source dropdown menu, then select your object type. Then, click the Dynamic page slug dropdown menu to select a property of that object type that will always be unique value. In our example, we’ve set the Data source to the Cars object type, and the page URL will use the value in each car record's VIN property because no two cars have the same VIN.
    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.
  6. Under Metadata, continue selecting the properties that will populate the page’s metadata. In our example, we’re setting the featured image to a property that will contain an image URL for a picture of each car.
  7. After setting up your metadata, at the top of the page, click the Content tab. 
  8. In the left sidebar, using the "Edit Modules" panel, add your module to the page.
  9. When ready, publish your page by clicking "Publish" in the upper right..

 

In our car example our listing page will be at: /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.

You've now successfully created dynamic pages based on your data source. Now, when a user updates a car record in HubSpot, the listing page and detail page will update automatically to reflect the changes. Newly created records of that object type will also automatically create new pages using the dynamic page slug and be linked to from the listing page.

Create a listing module

Depending on your use-case you may want the dynamic page's root URL to display a listing of your object's records.

In our car example, we would want the https://website.com/cars page to display the listing of all of the available cars.

To do this, you can create a new custom module that will act as a listing of the object records. Within this module, use the dynamic_page_crm_object_type_fqn variable to access the fully qualified name (fqn) of the page's selected object.

Tip: Naming the module something like "[object type] - listing" can make it clear what this module does. In our example we would name it "Car - listing".

You can use and modify the example code below to create a listing module:

HubL
{% if dynamic_page_crm_object_type_fqn %}{# dynamic listing page #}

    {% set cars = crm_objects(dynamic_page_crm_object_type_fqn, "limit=200","vin, price, picture, location, body_type, date_received, image") %}
    {# To see all of the properties your object during development, print it to the page with the |pprint filter #} 
    
    <div class="car__listing">
        {% for car in cars.results %}
            <div class="car__card">
              <a href="{{ request.path }}/{{ car.vin }}">
                <div class="car__image">
                    {% 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="car__details">
                    <div class="car__price">
                        Price: {{car.price |format_currency("en-US")}}
                    </div>
                    <div class="car__body">
                        Body Type: {{ car.body_type}}
                    </div>
                    <div class="car__distance">
                        Distance from Cambridge: <span class="car__dealer-location"> {{car.location}}</span> 
                    </div>
                    <div class="car__listing-date">
                        Time since listing: <span class="car__listing-date"> {{car.date_received }}  </span> 
                    </div>
        
                </div>
              </a>
            </div>
        {% endfor %}
    </div>
{% elif dynamic_page_crm_object %}
  <!-- Listing module is hidden when viewing a dynamic pages detail page. -->
{% else %}{# display simple listing for use on other pages #}
  {% set cars = crm_objects("p9307273_car", "limit=5","vin, price, location, body_type, date_received, image") %}
  {# To see all of the properties your object during development, print it to the page with the |pprint filter #} 
  <section aria-labelledby="object-listing-heading">
    <h3 id="object-listing-heading">
      Available Cars  
    </h3>
    <ul>   
    {% for car in cars.results %}
      <li><a href="/cars/{{ car.vin }}">{{ car.body_type }} ({{ car.price }}) in {{ car.location }}</a></li>
    {% endfor %}
    </ul>
  </section>
{% endif %}

In this code we are checking if the current page is a CRM object dynamic listing page. If it is, the module will display a listing of items. If it’s a detail page, the module won’t display anything. If it's another CMS page, we show a simple listing of results.

Now you can add this module to a dynamic CMS page or to any other page where you want a listing of CRM object records.

Learn more about creating listings with the crm_objects function.

More CRM object resources


Was this page helpful? *
This form is for feedback on our developer docs. If you have feedback on the HubSpot product, please share it in our Idea Forum instead.