Skip to main content

Experience Level: Intermediate | Tools Required: HubSpot CMS, jQuery, MixItUp | Related Topics: HubSpot Custom Modules, JavaScript, CSS, Portfolio Design

Are you looking to showcase your work with a beautiful, filterable portfolio gallery on your HubSpot website? In this tutorial, I'll walk you through creating a custom portfolio gallery module that allows for dynamic category filtering, responsive design, and elegant hover effects.

What We'll Build

Our portfolio gallery will:

  • Display client work in a responsive grid
  • Allow visitors to filter projects by category
  • Feature smooth animations and hover effects
  • Be fully customizable through the HubSpot module editor
Support dynamic categories without code changes

portfolio-gallery-blogspot-zeenko

Step 1: Create a New Custom Module

  1. In your HubSpot portal, navigate to Marketing > Website > Website Pages
  2. Click on Settings and select Custom Modules
  3. Click Create custom module
  4. Name your module "Portfolio Gallery" and select "Standard page" module type
  5. Click Create

Step 2: Set Up the Module Fields

Replace the default fields with this JSON schema:

json

[
{
"allow_new_line": false,
"default": "Our Work",
"id": "heading",
"label": "Heading",
"name": "heading",
"required": false,
"type": "text"
},
{
"children": [
{
"allow_new_line": false,
"id": "filter_categories.category_name",
"label": "Category Name",
"name": "category_name",
"required": true,
"type": "text"
},
{
"allow_new_line": false,
"help_text": "Used for filtering. Example: 'design_branding', 'development', 'support' (lowercase, use underscores for spaces)",
"id": "filter_categories.category_id",
"label": "Category ID (lowercase, use underscores for spaces)",
"name": "category_id",
"required": true,
"type": "text",
"validation_regex": "[a-z0-9_]+"
}
],
"id": "filter_categories",
"label": "Filter Categories",
"name": "filter_categories",
"occurrence": {
"max": 10,
"min": 1
},
"required": true,
"tab": "CONTENT",
"type": "group"
},
{
"children": [
{
"allow_new_line": false,
"id": "portfolio_items.client_name",
"label": "Client Name",
"name": "client_name",
"required": true,
"type": "text"
},
{
"default": {
"src": "",
"alt": null
},
"id": "portfolio_items.image",
"label": "Portfolio Image",
"name": "image",
"required": true,
"resizable": true,
"responsive": false,
"type": "image"
},
{
"children": [
{
"id": "portfolio_items.categories.category",
"label": "Category",
"name": "category",
"required": true,
"type": "choice",
"display": "select",
"multiple": false,
"placeholder": "Select a category",
"help_text": "Select a category for this portfolio item",
"dynamic_field_reference": "filter_categories.category_id"
}
],
"id": "portfolio_items.categories",
"label": "Categories",
"name": "categories",
"occurrence": {
"max": 10,
"min": 1
},
"required": true,
"tab": "CONTENT",
"type": "group"
},
{
"default": {
"url": {
"type": "EXTERNAL",
"href": ""
},
"open_in_new_tab": false
},
"id": "portfolio_items.link",
"label": "Portfolio Link",
"name": "link",
"required": false,
"type": "link"
}
],
"id": "portfolio_items",
"label": "Portfolio Items",
"name": "portfolio_items",
"occurrence": {
"max": 100,
"min": 1
},
"required": true,
"tab": "CONTENT",
"type": "group"
}
]

Step 3: Add the HTML Template

Replace the default HTML with:

html

<div class="portfolio-gallery">
<div class="wrap">
<h1></h1>

<div class="gallery-wrap">

<ul id="filters" class="clearfix">
<li><span class="filter active" data-filter="all">All</span></li>

</ul>

<div id="gallery">



</div><!--/gallery-->

</div><!--/gallery-wrap-->

</div>
</div>

Step 4: Add the CSS Styles

Add these styles to make your portfolio gallery look great:

css

/* Portfolio Gallery Module CSS */
.portfolio-gallery {
color: #212121;
line-height: 1.625;
}
.wrap {
margin: 0 auto;
max-width: 1200px;
width: 100%;
padding: 40px 20px;
}

.gallery-wrap,
#gallery {
overflow: hidden;
}
#filters {
margin: 0 0 30px;
padding: 0;
list-style: none;
overflow: hidden;
text-align: center;
}
#filters li {
display: inline-block;
margin: 0 5px 10px;
}
#filters li span {
display: block;
padding: 8px 20px;
text-decoration: none;
color: #212121;
cursor: pointer;
text-transform: uppercase;
font-size: 0.85rem;
font-weight: 600;
border-radius: 4px;
transition: all ease-in-out 0.2s;
}
#filters li:hover span {
color: #000;
background: rgba(222, 255, 42, 0.3);
}
#filters li span.active {
background: #deff2a; /* Brand color */
color: #212121;
}
.gallery-item {
float: left;
width: 33.333%;
padding: 15px;
position: relative;
z-index: 10;
display: none; /* Initially hidden, will be shown by MixItUp */
}
.inside {
position: relative;
overflow: hidden;
width: 100%;
height: 100%;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.details,
.overlay {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
opacity: 0;
}
.details {
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
transition: all 0.3s ease-in-out;
padding: 20px;
}
.details h2 {
color: #212121;
font-size: 1.5rem;
font-weight: 700;
letter-spacing: 0.5px;
text-align: center;
margin: 0 0 8px;
}
.details p {
color: #212121;
font-size: 0.9rem;
letter-spacing: 1px;
text-align: center;
margin: 0;
text-transform: uppercase;
}
.inside img {
float: left;
width: 100%;
transition: transform 0.5s ease;
}
.placeholder {
background: #f5f5f5;
height: 250px;
display: flex;
align-items: center;
justify-content: center;
color: #999;
}
.overlay {
background: rgba(222, 255, 42, 0.85); /* Brand color with opacity */
z-index: 1;
transition: all 0.3s ease-in-out;
}
.gallery-item:hover .details,
.gallery-item:hover .overlay {
opacity: 1;
}
.gallery-item:hover img {
transform: scale(1.05);
}
@media (max-width: 992px) {
.gallery-item {
width: 50%;
padding: 10px;
}

.details h2 {
font-size: 1.2rem;
}
}
@media (max-width: 576px) {
.gallery-item {
float: none;
width: 100%;
padding: 8px 0;
}

#filters {
display: flex;
flex-wrap: wrap;
justify-content: center;
}

#filters li {
margin: 0 3px 8px;
}

#filters li span {
padding: 6px 14px;
font-size: 0.8rem;
}
}

Step 5: Add JavaScript for Filtering

Finally, add this JavaScript to enable the filtering functionality:

javascript

$(function () {
// Wait for document ready
$(document).ready(function() {
console.log("Portfolio gallery initializing...");

// Initialize MixItUp
$('#gallery').mixItUp({
selectors: {
target: '.gallery-item',
filter: '.filter'
},
load: {
filter: 'all' // Show all items by default
},
animation: {
enable: true,
effects: 'fade translateZ(-100px)',
duration: 400,
easing: 'ease',
perspectiveDistance: '3000px',
perspectiveOrigin: '50% 50%'
},
callbacks: {
onMixStart: function(state) {
console.log("Mix operation started");
},
onMixEnd: function(state) {
console.log("Mix operation complete");
},
onMixFail: function(state) {
console.log("No matching items found");
}
}
});

// Add click handling to filter buttons
$('#filters .filter').click(function() {
// Remove active class from all filters
$('#filters .filter').removeClass('active');

// Add active class to clicked filter
$(this).addClass('active');
});
});
});

Step 6: Add Required JavaScript Libraries

This module requires two external JavaScript libraries:

  1. jQuery (if not already included in your theme)
  2. MixItUp for the filtering functionality

In your HubSpot template, go to the template editor and add these scripts in the head section

html

<!-- jQuery (if not already included in your theme) -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>

<!-- MixItUp Plugin -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/mixitup/2.1.11/jquery.mixitup.min.js"></script>

Step 7: Configure and Test Your Module

  1. Save your module and add it to a page
  2. Add at least 3-4 filter categories (such as "Design & Branding", "Development", "Support")
  3. Add several portfolio items, assigning them to different categories
  4. Preview your page to test the filtering functionality

Troubleshooting Tips

If your portfolio items aren't showing:

  1. Check Console for Errors: Open browser dev tools to look for JavaScript errors
  2. Verify jQuery and MixItUp: Make sure both libraries are loading properly
  3. Check Category IDs: Ensure they follow the lowercase_with_underscores format
  4. Inspect Gallery Items: Use the browser inspector to verify classes are being applied correctly

Customizing Your Portfolio Gallery

You can easily customize the appearance:

  • Change colors in the CSS (look for #deff2a and other color values)
  • Adjust animation speed by modifying the duration value in the JavaScript
  • Change the hover effect by modifying the .overlay and .details CSS

Conclusion

You now have a beautiful, dynamic portfolio gallery that showcases your work professionally and allows visitors to filter by category. The best part is, it's fully customizable - content editors can add or remove categories and portfolio items without touching any code.

Happy HubSpotting!

 

Tags:

Hubspot
Claudia Uztzu
Post by Claudia Uztzu
May 7, 2025 7:13:26 AM

Comments