Skip to content

Leveraging the Power of Dynamic Pages with HubSpot

In the rapidly evolving digital landscape, the need for personalised, engaging, and efficient web content is paramount. One of the ways businesses are meeting this demand is through the use of dynamic pages. But what exactly are dynamic pages and how can they be utilised to their full potential?

Dynamic pages in HubSpot are a type of web page that are dynamically created based on the URL. Typically one creates a listing page that will showcase various links to the dynamic pages. Below you will see a styled list that uses data in a HubDB table to provide links to the individual pages. We will show you the code later. Just realise that if we would add or amend one of the rows (= books) in the database, it would auto-update.

The cool thing however is not the listing page, but the dynamic pages themselves: we create one, and HubSpot does the rest.

So in our case if you click on a link, it will load a page that checks which book it will need to show the details from. This happens based on the last part of the URL, which we have set to include the name of the book in the listings page.

HubSpot's own documentation shows how to make a full page dynamic. We never use full pages but always modules. This allows more flexibility as you can mix and match standard content and smart content with dynamically generated content. The standard and smart content can later be amended by any marketeer without any need to understand code. This also means the developers can focus on just the part that they need to focus.

Here is what we cooked up for the listing:

So how did we create this?

In this case we started off with the simplest way to do it: create one single module that checks if it is on a dynamic page or not. And acts accordingly. This is following the example of the HubSpot documentation. In our example of dynamic pages build on custom objects we have actually split up the modules, so note that can be done just as well.
As you can see in the code below the module just includes 8 lines of HTML to present the 'details' page, and another 27 for the index page.

The same HubDB table that powers this page also powers the programmable email demo.

{% if dynamic_page_hubdb_row %}
  <!-- This will only execute if it is the books-detail page. NOT if it is the books index page -->
  <h1>{{ dynamic_page_hubdb_row.title }}</h1>
  <h3>by {{ dynamic_page_hubdb_row.author }}</h3>
  <div class="details-img">
    {% image "book-cover" label="Book cover" alt="Book cover {{ dynamic_page_hubdb_row.title }}" src="{{dynamic_page_hubdb_row[6].url}}" width="300" %}
  </div>
  {{ dynamic_page_hubdb_row.review }}
{% elif dynamic_page_hubdb_table_id %}
  <!-- This will only execute if it is the books index page -->
  <div class="card-grid">
    {# retrieve each row #}
    {% for row in hubdb_table_rows(module.hubdbtable) %}
      <a href="{{ row[9] }}" aria-label="Read more about {{ row.title }}">
        <div class="card">
          <div class="card-img">
            {% image "book-cover" label="Book cover" alt="Book cover {{ row.title }}" src="{{row[6].url}}" width="300" %}
          </div>
          <div class="card-body">
            <p class="card-author">
              Author: {{ row.author }}
            </p>
            <p class="card-genre">
              Genre:
              {# loop through genres #}
              {% for genre in row[7] %}
                {{ genre.name }}{% if not loop.last %}, {% endif %}
              {% endfor %}
            </p>
          </div>
        </div>
      </a>
    {% endfor %}
  </div>
{% endif %}
.card {
  background: white;
  transition: background-color 0.3s ease;
  border-radius: 15px;
  box-shadow: 0 10px 20px rgba(0, 0, 0, 0.25);
  margin: 20px;
  max-width: calc(100% - 40px);
  overflow: hidden;
  display: inline-block;
}

.card:hover {
  background: rgba(211, 254, 255, 0.5);
}

.card a {
  color: inherit;
  text-decoration: none;
  display: block;
}

.card-img {
  width: calc(100% - 30px);
  height: 450px;
  overflow: hidden;
  margin: 15px;
  border: 1px solid #4eaae3;
  box-shadow: 0 0 5px 5px rgba(211, 254, 255, 0.85);
  box-sizing: border-box;
}

.card-body {
  padding: 20px;
}

.card-title {
  margin: 0 0 10px 0;
}

.card-genre, .card-author {
  font-size: 0.9em;
  color: #666;
}

.card-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}

.details-img img {
    float: right;
    margin-left: 20px;
    margin-bottom: 20px;
    border: 1px solid #4eaae3;
    box-shadow: 0 0 5px 5px rgba(211, 254, 255, 0.85);
}
[
 {
  "display_width": null,
  "id": "bba54aae-4637-3ef7-25cb-2472b159446c",
  "label": "HubDB table",
  "locked": false,
  "name": "hubdbtable",
  "required": true,
  "type": "hubdbtable"
 }
]