How to build dynamic pages using CRM objects

Last updated:

You can build CRM object dynamic pages using standard HubSpot objects, such as Products, or custom objects (Enterprise only). Dynamic pages consist of a listing page view and a unique details page for each record. The listing page displays a list of all records in the object, and the details pages display information about each record based on the record's property values. 

You can also build dynamic pages using HubDB. Learn more about the different types of dynamic pages.

Building a CRM object dynamic page requires four steps:

  1. Prepare your CRM object with data you want to display.
  2. Create a custom module to display the data.
  3. Create a listing module to display all records on a listing page.
  4. Add the modules to a drag and drop page and select the object as the data source.

This tutorial will walk through how to build a CRM object dynamic page using the example of a Cars 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. 

Prerequisites

To build CRM object dynamic pages, you’ll need:

Prepare your CRM object

When using CRM object dynamic pages, each details page that is automatically created is unique to each record in your CRM. To define the metadata for the details pages, such as page URL slug and page title, you'll need to map properties to definitions within the page editor.  

In the table below, learn about the page properties. It's not required to have properties with these exact names, and you can use existing properties for the metadata.

Dynamic page properties
Dynamic page propertyProperty typeDescription
Dynamic page slug
Required
text

The slug that will be appended to the CMS page URL for each details page. The property you map to this definition needs to be a text field with "hasUniqueValue": true, which you can set using the 

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 -.

 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.

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.

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

Individual detail pages will use this module to display the object record data. In our example, the module will display details for an individual car's page.

Within this module, you'll use the dynamic_page_crm_object variable to access the data stored to the current dynamic page's object instance.

To set up the details module:

Tip: naming the module something like [object type] - detail can make it clear what this module does. For example, Car - detail.

  • In the module.html field, 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, 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 page editor.

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 of car. To fit your use case, you can update the module.car references in the code to your object's name.

After creating this module, you’ll then create the listing module to define the information that will appear on the general 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, 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. For example, 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.

Page setup

With the modules created, you can now add them to a page, then select the object as the data source.

  1. Create a new page, selecting a page template that has a drag and drop area or flexible column. 
  2. In the page editor, click the Settings tab.
  3. Under Page URL, click the pencil icon to edit the page's URL. Set the URL to where you want your listing page to appear. In our car example our listing page will be at: /cars
  4. Click Advanced options, then scroll to the Dynamic pages section.
  5. Under Dynamic Pages, click the Data source dropdown menu, then select your object type. Then, click the Dynamic page slug dropdown menu and select the property to use as the page slug. Only properties set to  "hasUniqueValue":true will be available for selection. In our example, the page URL will use the VIN property, which will generate detail pages with the URL slug of: /cars/[VIN value]

    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 contains 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 to return to the editor.
  8. In the left sidebar, in the Add tab, search for your detail and listing modules, then drag them into the page editor.
  9. To preview your page, click Preview in the upper right.
  10. When ready, publish your page by clicking Publish in the upper right.

You've now successfully created dynamic pages based on your data source. With our example page set up, when a user updates a car record in HubSpot, the listing and detail pages 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.

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.