Power Plus - List Module
The central engine module that connects a datasource to a renderer, manages paging, handles browser history and URL parameters, coordinates all filter/sort/search controls, and provides a debug mode that prints the available data schema to the browser console.
The List module is the core engine of the Power Plus framework. I t coordinates the datasource (which provides the data), the renderer (which displays it), and all the controls (which let visitors interact with it).
When a page loads, the List module:
1. Discovers its datasource and renderer by looking up their names in the plugin registry.
2. Waits for all connected controls (filters, sort, search) to finish initializing.
3. Builds a request object from the combined state of all controls.
4. Asks the datasource to fetch data matching that request.
5. Passes the results to the renderer for display.
6. Manages paging, loading states, error states, empty states, and browser history.
Configuration
|
Field |
Type |
Description |
|---|---|---|
|
Name |
Text |
A unique identifier for this list instance (e.g. |
|
Renderer Name |
Text |
Must exactly match the Name field of a Renderer module placed somewhere on the same page. If no renderer with this name is found, the list will show an error. |
|
Datasource Name |
Text |
Must exactly match the Name field of a Datasource module placed somewhere on the same page. If no datasource with this name is found, the list will show an error. |
|
Paging |
Choice |
Controls how additional results are loaded. |
|
URL Paging (SEO) |
Boolean |
When enabled, the current page number is appended to the URL as a query parameter (e.g. |
|
URL Paging Parameter |
Text |
The name of the URL query parameter used for paging. Default: |
|
Selection |
Choice |
Enables item selection behavior. |
Default Filters
You can add default filters directly in the List module. These filters are always applied to every request — they cannot be changed or removed by the visitor. They are combined with any visitor-facing filter controls using AND logic.
This is useful for pre-filtering data. For example, on a “Featured Products” page, you might add a default filter is_featured
== true so only featured items ever appear, regardless of what other filters the visitor selects.
|
Filter Field |
Description |
|---|---|
|
Property Name |
The path to the data field to filter on. For HubDB data, this is typically prefixed with |
|
Operator |
The comparison operator. See the Filter Operators Reference for the full list. Common operators: |
|
Value |
The value to compare against. This can be a plain string, a number, or a JSONata expression enclosed in |
Default Sorts
You can add default sorts that define the initial ordering of results. These are applied when no visitor-selected sort is active, and they are appended after any visitor-selected sorts.
|
Sort Field |
Description |
|---|---|
|
Property Name |
The data field to sort by. For HubDB: |
|
Order |
|
Translations / Text
These fields let you customize the text that the list displays in various states. All text fields support rich text (HTML) and JSONata expressions, so you can create dynamic, context-aware messages.
|
Field |
Description |
|---|---|
|
Load Button Text |
The label for the “Load More” paging button. Default: |
|
Empty Text |
Rich text displayed when the list has zero results after filtering. This is your opportunity to guide the visitor — suggest clearing filters, trying different search terms, or provide a link to reset. Supports JSONata so you can reference the current filter state in the message. |
|
Error Text |
Rich text displayed when something goes wrong (network error, invalid data, etc.). In debug mode, technical error details are appended automatically. Supports JSONata with access to the |
Style Options
|
Field |
Description |
|---|---|
|
Min Height |
Minimum height of the list container in pixels. Setting this prevents the page from “jumping” while data is loading — the container reserves space even before the first results appear. A value of 200–400px is typical. |
|
Color Scheme |
|
|
Animation |
Enable AOS (Animate on Scroll) effects. When enabled, the list container fades or slides into view as the visitor scrolls to it. You can choose the animation type (fade, slide-up, zoom, etc.) and set a delay. |
|
Button Style |
The CTA style and size for the “Load More” button. Uses the POWER THEME’s CTA system, so you can choose from all available button styles (solid, outline, ghost, etc.) and sizes (small, regular, large). Also includes alignment options (left, center, right). |
How Paging Works
Paging determines how additional results beyond the first page are loaded:
-
Button: After the initial items are rendered, a “Load More” button appears at the bottom of the list. When the visitor clicks it, the next batch of items is fetched and appended below the existing ones. The button disappears when all items have been loaded. This is the recommended default for most use cases because it gives visitors explicit control over when more content loads.
-
Auto (Infinite Scroll): The framework watches for the visitor to scroll near the bottom of the list. When the scroll position reaches the “load more” trigger element, the next batch is fetched and appended automatically. This continues until all items are loaded. Infinite scroll works well for browsing-heavy pages like product catalogs, where visitors tend to scroll continuously.
-
URL Paging (SEO enhancement): When enabled alongside Button or Auto paging, the current page number is written into the URL as a query parameter. For example, after loading the second page, the URL might become
?page=2. This has two benefits: (1) search engines can crawl each page separately, improving SEO for large datasets; (2) visitors can bookmark or share a specific page of results. The “Load More” button gets a proper<a href="?page=3">link that crawlers can follow. -
Disabled: All matching items are loaded and rendered in a single request. Only use this for small datasets (under ~50 items) or when the renderer itself handles scrolling (like the Slider renderer).
Debug Mode: Schema Console Output
When you’re setting up a new list, you need to know what data fields are available so you can configure your renderer and filters correctly. The framework provides a built-in debug mode that prints the complete data schema to your browser’s developer console.
How to Activate Debug Mode
Debug mode is active in two situations:
-
HubSpot Preview — When viewing the page in HubSpot’s page preview (the URL contains
hubspotpreview). -
Debug URL Parameter — When viewing the live page with
?hsDebug=trueappended to the URL (thehs_previewparameter must also be present).
What Gets Logged
When debug mode is active, the framework logs several things to the browser console every time data is loaded:
1. Available JSONata Placeholders
Each renderer prints a formatted list of all available data fields, their types, and the exact [[ ... ]] placeholder syntax you can use in your renderer configuration. Open your browser’s developer console (F12 → Console tab) and look for a message like:
Available JSONata placeholders in Block Renderer 'product_renderer' :
string [[ object.id ]]
string [[ object.name ]]
string [[ object.hs_price_eur ]]
string [[ object.hs_sku ]]
string [[ object.description ]]
string [[ object.category ]]
string [[ object.hs_images ]]
For HubDB datasources, the output shows the nested values structure:
Available JSONata placeholders in Block Renderer 'card_renderer' :
string [[ object.id ]]
string [[ object.createdAt ]]
string [[ object.updatedAt ]]
object [[ object.values ]]
string [[ object.values.name ]]
string [[ object.values.description ]]
object [[ object.values.image ]]
string [[ object.values.image.url ]]
string [[ object.values.image.altText ]]
object [[ object.values.category ]]
string [[ object.values.category.id ]]
string [[ object.values.category.name ]]
string [[ object.values.category.label ]]
boolean [[ object.values.featured ]]
string [[ object.values.link ]]
[object] [[ object.values.tags ]]
string [[ object.values.tags[0].name ]]
string [[ object.values.tags[0].label ]]
The format is: type followed by the exact placeholder expression you can copy-paste into your renderer field configuration. Array types are shown as [type] and include an example accessor with [0].
2. Current State
After each data load, the console logs the current list state object:
New State:
{ filters: { category_filter: ["electronics"], size_filter: ["large"] }, sorts: { product_sort: ["price_asc"] } }
This shows exactly which filter options and sort options are currently selected, which is helpful for debugging state management issues.
3. Error Details
When an error occurs in debug mode, detailed error information is shown both in the console and appended to the on-page error message. This includes: - The error message - The problematic token (for JSONata expression errors) - The position in the expression where the error occurred - The error code
This makes it much easier to diagnose issues like typos in JSONata expressions or misconfigured property names.
Using the Schema Output to Configure Your Page
Here’s the recommended workflow for setting up a new list:
-
Add the datasource module to your page and configure it with the URL/table.
-
Add any renderer module to the page (even with default/empty field configuration).
-
Add the List module and wire them together by name.
-
Preview the page in HubSpot (or open it with
?hsDebug=true). -
Open the browser console (F12 → Console) and look for the “Available JSONata placeholders” message.
-
Copy the placeholder expressions you need and paste them into your renderer field configuration.
-
For filters, look at the schema output to find the correct Property Name paths (e.g.
values.category.namefor a HubDB dropdown column).
This is especially valuable when working with: - External APIs where you’re not sure of the exact response structure - HubDB tables with many columns and nested types (images, dropdowns, multi-selects) - CRM objects where property names may not be obvious - JSONata-transformed data where the output shape depends on your query
Disabling Debug Mode in Production
Debug mode is never active on production pages viewed by regular visitors. It only activates in HubSpot’s preview environment or when the specific debug URL parameters are present. There is nothing you need to do to disable it — it’s off by default.
Additionally, caching is disabled in debug mode. CRM and Cision datasources normally cache responses in memory and localStorage to reduce API calls, but in debug mode all caching is bypassed so you always see fresh data during development.
Examples
Basic Product List
The simplest configuration: a CRM datasource, a block renderer, and infinite scroll paging.
List Module:
Name: product_list
Datasource Name: product_datasource
Renderer Name: product_renderer
Paging: Auto (infinite scroll)
When combined with a CRM datasource (endpoint: products/list) and a Block renderer configured with product image, name, and price fields, this creates a scrollable product catalog.
Featured Items Only (Pre-Filtered)
Show only items where the featured checkbox is enabled. This is useful for hero sections or curated spotlights on a homepage.
List Module:
Name: featured_list
Datasource Name: hubdb_datasource
Renderer Name: featured_renderer
Paging: Disabled
Filters:
- Property Name: values.featured
Operator: ==
Value: 1
Because paging is disabled, all matching featured items appear at once. The default filter ensures only featured items are shown — visitors cannot override this.
Related Products (Exclude Current)
On a product detail page, show products from the same category but exclude the product currently being viewed. This uses HubL template variables ( and ) that HubSpot resolves at page render time.
List Module:
Name: related_products
Datasource Name: product_datasource
Renderer Name: product_renderer
Filters:
- Property Name: category
Operator: ==
Value:
- Property Name: id
Operator: !=
Value:
The two default filters work together: the first includes only products in the same category, and the second excludes the current product so it doesn’t appear in its own “related” section.
Custom Empty State with Reset Action
Use JSONata and HTML in the empty text to create a helpful, interactive message when no results match:
Empty Text:
<h4>No results found.</h4>
<p>We couldn't find anything matching your current filters and search.
Try adjusting your selections or
<a class="listStateButton" state="{}">clear all filters</a>
to start over.</p>
The special CSS class listStateButton combined with the state="{}" attribute creates a clickable link inside the empty message that resets all filter, sort, and search state when clicked. The {} empty object means “clear everything”. You can also use inputValueButton with inputQuery and value attributes to set specific form input values from within the message.
Custom Error State with Context
Error Text:
<h4>Something went wrong.</h4>
<p>We had trouble loading the data. Please try again in a few moments.</p>
In debug mode, the framework automatically appends technical error details (message, token, position) below this text, which helps developers diagnose issues without needing to open the console.
State Management
The List module maintains a comprehensive state management system that tracks the selections of all connected controls and synchronizes them across browser history and URL parameters.
How State Flows
When any control changes (a filter is selected, a sort is chosen, a search term is entered):
-
The list collects the current state from every connected control by calling each control’s
appendRequest()method. -
It builds a combined request object with filter groups, sorts, and query text.
-
It pushes the new state into the browser history (via
window.history.pushState()), so the back button works. -
If any controls have URL parameters enabled, the URL is updated with the new parameter values.
-
The list sends the request to the datasource and renders the results.
Browser History Integration
When a visitor uses the browser’s back and forward buttons, the list intercepts the popstate event, restores the previous state to all controls, and reloads the data. This means filter selections, sort choices, and search terms are all preserved through navigation.
URL Parameters
Controls can optionally store their state in URL query parameters. This makes filtered views shareable — a visitor can copy the URL (e.g. https://yoursite.com/products?category=shoes&sort=price_asc&q=running) and share it. When someone opens that URL, the list automatically restores all the control states from the parameters.
To enable URL parameters for a control, set Use in URL to true and provide a URL Parameter Name. Choose short, descriptive names: category, sort, q, page, etc.
State Listeners (Showing/Hiding Sections)
Other page elements can react to the list’s current state (loading, loaded, empty, error) using CSS classes. Add the class pwr-list-state-listener--{list_name} to any HTML element, and the framework will automatically add one of these state classes:
-
pwr-list-state--loading— data is currently being fetched -
pwr-list-state--loaded— data has been loaded and rendered -
pwr-list-state--empty— the data loaded successfully but zero items matched -
pwr-list-state--error— an error occurred during loading
The most common use is hiding a section when the list is empty:
.pwr-list-state-listener--wishlist { display: none; }
.pwr-list-state-listener--wishlist:not(.pwr-list-state--empty) { display: block; }
This CSS hides the element by default, then shows it when the list has results (is not in the empty state). The Wishlist uses this pattern to hide the “Get A Quote” button and the “Recommended Products” section when the cart is empty — there’s no point showing a checkout button if there’s nothing in the cart.
You can also use this to show different content during loading:
.pwr-list-state-listener--my_list.pwr-list-state--loading .loading-message { display: block; }
.pwr-list-state-listener--my_list.pwr-list-state--loaded .loading-message { display: none; }
JavaScript Events
The list also dispatches custom DOM events that you can listen for in custom JavaScript:
-
onListLoading— fired when the list starts loading data -
onListLoaded— fired when data has been loaded and rendered -
onListEmpty— fired when zero results are returned -
onListError— fired when an error occurs -
onListReload— fired when the list is asked to reload (by a control change)