The standard HubSpot blog module was limiting in terms of real-time filtering, tag-based categorization, and client-side pagination. I needed to implement a fully dynamic blog listing page where users could:
Filter blogs by tags.
Maintain filter state even after navigating between pages (via localStorage
).
Paginate through blog results without reloading the entire page.
The goal: improve UX with fast, persistent, and dynamic navigation while retaining all existing backend data structure and design.
HubSpot’s CMS allows for some backend filtering through Hubl, but doesn’t retain tag state or paginate dynamically on the client side. I also had to ensure:
No existing functionality is broken.
No extra APIs or external libraries unless necessary.
All blog data (5000
entries) is fetched and manipulated client-side.
Using:
{ % set contents = blog_recent_posts(module.blog_field, 5000) % }
I fetched the full blog list within Hubl so it could be accessed in the frontend via data-*
or by embedding a JSON blob.
I pulled in blog tags with:
{ % set my_tags = blog_tags(module.blog_field, 50) % }
Then dynamically rendered buttons for each tag, attaching dataset attributes and event listeners for interaction.
localStorage.setItem('activeFilter', selectedTag);
This allowed me to retain the selected tag even after the user navigated or refreshed the page.
On page load, I read from localStorage
and applied the tag-based filter to the full blog list.
I implemented pagination by:
Chunking the filtered blog array into page-sized arrays.
Rendering only the blogs for the active page.
Updating the page number in localStorage
too, so both filter and page state were retained.
const blogsPerPage = 6;const filteredBlogs = getFilteredBlogs(); // based on selectedTagconst paginatedBlogs = paginate(filteredBlogs, currentPage);
During preview, some blogs were showing a date of 1970, which is a default UNIX epoch fallback when null
or undefined
. I clarified that this was only a preview issue and ensured once published, the correct publish_date
would appear.
To reassure stakeholders, I added a note in the UI or documentation:
“If you see a date like 1970, don’t worry — this is a preview artifact. The correct publish date will be applied after publishing.”
Dynamic filtering with persistent state.
Pagination without reloading or server requests.
Maintained full content accessibility.
UX became smoother, especially for content-heavy blogs.
No reliance on external APIs or plugin scripts.
Combining CMS templating (Hubl) with frontend logic gives best-of-both-worlds flexibility.
localStorage
is a great lightweight tool for state persistence.
Even within a CMS, it's possible to build interactive, responsive interfaces with careful planning and control.