Build dynamic pages using CRM objects

Last updated:
  • Marketing Hub
    • Enterprise
  • CMS Hub
    • Professional or Enterprise

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.

You can learn more about building data-based CMS pages in HubSpot Academy's CMS Data-Driven Content course.

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 Car custom object, which stores data on individual cars at a dealership. 


The following are required for building dynamic pages using CRM objects:

  • For custom object dynamic pages, you'll need a CMS Hub Enterprise subscription, or a Marketing Hub Enterprise account with CMS Hub Professional.
  • For standard CRM object dynamic pages, you'll need a CMS Hub Professional or Enterprise account.
  • An understanding of how to create custom modules
  • A standard or custom object to use as a data source. You'll also need user permission for the object, such as Companies or Custom Objects access. 
  • Records created under the object you're using, such as individual products or custom object records. Records can be created in HubSpot’s UI or via API endpoints.

Prepare your CRM object

Each dynamic page will pull its metadata from the properties of the individual records of your selected CRM object. The values contained in these properties should uniquely identify the content of the page to stand out in search results. Metadata includes:

  • Page slug
  • Page title
  • Meta description
  • Featured image

For the page title, meta description, and featured image, you can use any of the object's existing single-line text properties. However, the dynamic page slug must be a property configured with "hasUniqueValue": true. Existing properties cannot be updated with this configuration, so you will need to create a new property for this field. For custom objects, create a new property by updating the object schema. For standard objects, create a new property by using the properties API. 

Dynamic page properties
Dynamic page propertyProperty typeDescription
Dynamic page slug

The slug that will be appended to the CMS page URL for each details page. The property needs to be configured with "hasUniqueValue": true.

The property value needs to be URL friendly, and must contain:

  • All lowercase characters.
  • No spaces or special characters, except -.
  • No leading slash. For example:
    • Incorrect: /property-value
    • Correct: property-value
Page Title

The page's title.

Meta description

The page's meta description.

Featured image

The page's featured image, which will appear when the page is shared.

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.

Please note: content search will index up to 10,000 CRM records per set of dynamic pages. For example, if you build a set of dynamic pages based on a CRM object that has 15,000 records, only 10,000 of those pages will be indexed in the search feature. If you create another set of dynamic pages based on the same object, that set of pages will also be limited to 10,000 search-indexed pages.

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:
{% if dynamic_page_crm_object and ! %}{# 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="" 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: {{}} </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 = %} {# 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="" 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 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 example, we would want the 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 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:

{% 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 }}/{{ }}"> <div class="car__image"> {% if car.image %} <img src="{{car.image}}" alt="{{car.year}} {{car.make}} {{car.model}}"> {% else %} <img src="" 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.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. Then, click the Dynamic page slug dropdown menu and select the property to use as the page slug. Only single-line text 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 article helpful?
This form is used for documentation feedback only. Learn how to get help with HubSpot.