How to build multilevel dynamic pages with HubDB

Last updated:
APPLICABLE PRODUCTS
  • CMS Hub
    • Professional or Enterprise

dynamic website page is a CMS page whose content changes based on the path of the URL requested by an end user. HubDB already allows you to store, filter, and display data in your HubSpot website pages. Multilevel dynamic pages take this concept further, allowing you to create up to five levels of pages within one dynamic template.

Each dynamic page includes its own unique, SEO-friendly URL, and offers page-specific analytics.

Before you begin you will need:

  • Some prior knowledge of HubSpot's CMS, HTML, and HubL
  • CMS Hub Pro or Enterprise

Please note that this tutorial assumes you already have multiple HubDB tables created. Please see the HubDB documentation if you are unfamiliar with HubDB or want to create your first HubDB tables.

1. Enable child tables in your table's settings.

Navigate to HubDB in your HubSpot portal, and edit the table you would like to be a parent of other tables. In the table settings, ensure "Allow child tables" and "Enable pages for child tables" are both selected.

child table settings screen showing all checkboxes checked

This will add a new "Child Table" column to select other published HubDB tables in your portal.

2. Select child tables for each row

Each row in your parent table can now be linked to a child table.

A HubDB table can be selected as a child table only if the table setting "Enable creation of dynamic pages using row data" has been enabled.

parent table with child tables selected

The child tables can have their own unique properties.

child tables have their own unique properties

When setting multilevel dynamic pages, the page paths for each row in the child table will be "parent_path/child_path".

For example, the page path for the "apple" row will be "page_path/food/apple". Setting "Enable pages for child tables" to true means the intermediate paths ("page_path/food" and "page_path/beverage") also resolve to the dynamic template, and you can build separate listing pages for those subtypes.

If you would rather have those intermediate routes not resolve and return a 404 page, then you can uncheck "Enable pages for child tables".

3. Create the multilevel template

Previously dynamic pages only supported two levels, the top-level listing page and the dynamic page generated from the row. Child tables allows you to create up to a max of five levels of pages within one dynamic template.

To separate out the different template levels, you can use the dynamic_page_route_level HubL variable which starts at 0, for the top-level template and increments for each table layer:

HubL
{% if dynamic_page_route_level == 0 %}
	Top Level Template
{% elif dynamic_page_route_level == 1 %}
	Parent table template (/food /beverage)
{% elif dynamic_page_route_level == 2 %}
	Child table template (/food/banana etc., /beverage/soda etc.)
{% endif %}

4. Populate the top-level template

Each template level may be doing something unique. In the top level template, we would like to list the child rows and group them by parent category. First we get the category rows using dynamic_page_hubdb_table_id. Then we can utilize the hs_child_table_id property of each category row to get the table IDs of the child tables. Finally we use those table IDs to list the child rows under each parent category:

Note: It is important to try to keep your child table columns and their internal names the same. If they are not the same, you will need to use conditional logic to render unique content for a given table.

HubL
{% if dynamic_page_route_level == 0 %}
	<h1>Categories</h1>
    {% set rows = hubdb_table_rows(dynamic_page_hubdb_table_id) %}
    {% for row in rows %}
     	<h2><a href="{{ request.path }}/{{ row.hs_path }}">{{ row.hs_name }}</a></h2>
      	{% set childRows = hubdb_table_rows(row.hs_child_table_id) %}
      	{% for childRow in childRows %}
        	<li><a href="{{ request.path }}/{{ row.hs_path }}/{{childRow.hs_path}}">{{ childRow.hs_name }}</a></li>   
      	{% endfor %}             
     {% endfor %}
{% endif %}

5. Populate the dynamic-level templates

For each template level we wish to show, we need to define a template. It is sometimes useful in child templates to access data in the parent row. For example, when we resolve to "food/banana" the dynamic_page_hubdb_row variable will be set the "banana" row, however we would still like to access data from the "food" row. We can use hs_parent_row value on the dynamic_page_hubdb_row to retrieve the parent row:

HubL
{% if dynamic_page_route_level == 1 %}
	<h1>Categories</h1>
	<h2>{{dynamic_page_hubdb_row.hs_name}}</h2>
    {% set rows = hubdb_table_rows(dynamic_page_hubdb_row.hs_child_table_id) %}
    {% for row in rows %}
    	<li><a href="{{ request.path }}/{{ row.hs_path }}">{{ row.hs_name }}</a></li>                
    {% endfor %}
{% elif dynamic_page_route_level == 2 %}
	<h1>Categories</h1>
    <h2>{{dynamic_page_hubdb_row.hs_parent_row.hs_name}}</h2>
    <h3>{{dynamic_page_hubdb_row.hs_name}}</h3>
{% endif %}

The final step is to create a page from the multilevel template and link the top-level parent table to the page under the "Advanced Options" section of the page editor. Here is an example nested dynamic page generated from the template created in this tutorial.

hubdb field in page settings

Share your feedback

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.