HubSpot Code Block | HubDev

Case Study: Dynamic Blog Filtering & Pagination in HubSpot Using localStorage

Written by HubDev | Jun 20, 2025 11:00:15 PM

Problem Statement

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.

Understanding the Constraints

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.

My Role & Solution Execution

1. Fetching Blog Posts

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.

2. Generating Tags for Filtering

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.

3. Storing Filter State with localStorage

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.

4. Client-Side Pagination

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);

5. Final Touch: Date Handling Issue

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.”

Outcome

  • 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.

What I Learned

  • 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.