In this guide, we’ll walk through how to build a dynamic filter module using HubSpot Custom Objects. This flexible tool allows visitors to:
This functionality is perfect for businesses that host regional events and want to improve user experience by making it easy to find nearby events through a customized filtering tool built on HubSpot Custom Objects.
Here’s a high-level breakdown of what we’ll build:
We’ll use a HubSpot serverless function to retrieve event records from your HubSpot Custom Objects. This is a scalable solution that allows you to securely access HubSpot CRM data without exposing sensitive details on the front end.
filter.js - HubSpot Serverless Function Example:
const axios = require('axios');
exports.main = async (context, sendResponse) => {
const headers = new Headers();
headers.append("Access-Control-Allow-Origin", "*");
headers.append("Content-Type", "application/json");
headers.append("Authorization", "Bearer YOUR_BEARER_TOKEN");
const fetchEvents = async () => {
const today = new Date();
const startDate = new Date(today);
const endDate = new Date(today);
endDate.setDate(today.getDate() + 28);
let allResults = [];
let hasMore = true;
let after = undefined;
while (hasMore) {
const raw = JSON.stringify({
filterGroups: [{
filters: [{
propertyName: "start_time",
operator: "BETWEEN",
value: startDate.toISOString(),
highValue: endDate.toISOString()
}]
}],
properties: [
"id", "event_id", "venue_title",
"latitude", "longitude", "venue_address_1",
"venue_city", "venue_state", "venue_zip_code",
"start_time", "event_status"
],
after
});
const response = await fetch(
"https://api.hubapi.com/crm/v3/objects/Your_Function_NAME_/search",
{
method: "POST",
headers: headers,
body: raw
}
); const data = await response.json();
allResults = allResults.concat(data.results || []);
hasMore = !!data.paging?.next;
after = data.paging?.next?.after;
}
return allResults;
};
const events = await fetchEvents();
sendResponse({
statusCode: 200,
body: {
events
}
});
};
Note: The Authorization
header includes your Bearer token for secure access to HubSpot’s CRM API. Make sure to replace YOUR_BEARER_TOKEN
with your actual token from HubSpot’s private app configuration. Learn more about Hubspot API's here.
The HTML structure forms the core of your search functionality. This includes a dropdown for radius selection, a ZIP code input field, a search button, and containers for displaying filtered results in both map and list views.
<script async src="https://maps.googleapis.com/maps/api/js?key=MAP_API_KEY&libraries=places,geometry"></script>
<div class="search-interface">
<div class="search-controls">
<select id="radius-select" class="hs-select">
<option value="5">5 miles</option>
<option value="20" selected>20 miles</option>
<option value="30">30 miles</option>
</select>
<input type="text"
id="zip-input"
placeholder="Enter ZIP code"
pattern="\\d{5}"
title="5-digit ZIP code">
<button id="search-btn" class="hs-button">
Search Events
</button>
</div>
<div class="results-container">
<div class="map-view" id="map"></div>
<div class="list-view" id="events-list"></div>
<div class="no-results">
</div>
</div>
</div>
Tip: This layout is fully customizable through HubSpot's drag-and-drop editor when integrated as a custom module.
This JavaScript powers the dynamic filtering. It integrates with Google Maps for geolocation and mapping, fetches filtered event data from your serverless function, and dynamically updates the UI based on user search inputs.
async function performSearch() {
const zip = document.getElementById('zip-input').value;
selectedRadius = document.getElementById('radius-select').value;
if (!zip) {
alert('Please enter a ZIP code.');
return;
}
try {
const response = await fetch(`/_hcms/api/YOUR_CUSTOM_FUNCTION_JSON_NAME?zip=${zip}&radius=${selectedRadius}`);
const events = await response.json();
if (events.length === 0) {
showNoResults();
return;
}
const eventsWithDistances = calculateDistances(events, { lat: 39.8283, lng: -98.5795 });
displayEvents(eventsWithDistances);
plotEventsOnMap(eventsWithDistances, zip);
} catch (error) {
console.error('Search failed:', error);
showErrorState();
}
}
function displayEvents(events) {
const eventList = document.getElementById('event-list');
eventList.innerHTML = '';
if (events.length === 0) {
eventList.innerHTML = '<p>No events found within the selected radius.</p3>';
return;
}
events.forEach(event => {
const eventElement = document.createElement('div');
eventElement.classList.add('event');
eventElement.innerHTML = `
<h3>${event.name}</h3>
<p><strong>Description:</strong> ${event.description}</p>
<p><strong>Distance:</strong> ${event.distance.toFixed(2)} miles</p>
<p><strong>Location:</strong> ${event.location}</p>
<hr>
`;
eventList.appendChild(eventElement);
});
}
function showNoResults() {
const eventList = document.getElementById('event-list');
eventList.innerHTML = '<p>No events found within the selected radius.</p>';
}
function showErrorState() {
const eventList = document.getElementById('event-list');
eventList.innerHTML = '<p>Error occurred while fetching events. Please try again later.</p>';
}
Pro tip: You can further customize the displayEvents()
Customize fields in your module.
This CSS ensures the functionality looks great on both desktop and mobile devices. Customize it further to fit your brand guidelines.
.search-interface {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
.results-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
margin-top: 2rem;
}
.map-view {
height: 600px;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.event-card {
padding: 1.5rem;
background: #fff;
border-radius: 8px;
margin-bottom: 1rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
}
@media (max-width: 768px) {
.results-container {
grid-template-columns: 1fr;
}
}
Suggestion: Tailor this CSS to create a unique, branded experience for your HubSpot functionality via Custom Objects.
By following this guide, you’ll be able to build a filter via HubSpot Custom Objects that helps your audience easily discover, register, or join waitlists for your events. The ZIP code filtering and customizable radius ensure they only see relevant events, improving the user experience and increasing registration rates.