Advanced HubSpot CMS Tips and Tricks
Ready to master HubSpot CMS? These advanced tips will help you build better, faster, and more effective websites.
Advanced HubL Techniques
1. Dynamic Module Loading
Load modules conditionally to improve performance:
jinja2
{% if page.content_group_id == 123 %}
{% module "special_cta" path="/modules/special-cta" %}
{% endif %}
2. Custom Filters
Create reusable data transformations:
jinja2
{% set formatted_date = content.publish_date|datetimeformat('%B %d, %Y') %}
{% set reading_time = content.post_body|wordcount / 200 %}
3. Macros for Reusability
Build reusable components:
jinja2
{% macro button(text, url, style="primary") %}
{{ text }}
{% endmacro %}
{{ button("Learn More", "/contact", "primary") }}
4. Advanced Loops with Filters
jinja2
{% set featured_posts = blog_recent_posts(
'default',
10,
blog_author=author_slug
) %}
{% for post in featured_posts if post.featured_image %}
{# Display post #}
{% endfor %}
5. Context Variables
Access powerful context data:
jinja2
{# Page context #}
{{ request.path }}
{{ request.query_dict.utm_source }}
{# User context #}
{{ contact.firstname }}
{{ contact.lifecycle_stage }}
{# Site context #}
{{ site_settings.company_name }}
{{ site_settings.company_logo }}
Performance Optimization
1. Lazy Load Below-the-Fold Content
javascript
// In custom module JavaScript
if ('IntersectionObserver' in window) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// Load content
entry.target.classList.add('loaded');
observer.unobserve(entry.target);
}
});
});
document.querySelectorAll('.lazy-section').forEach(section => {
observer.observe(section);
});
}
2. Preload Critical Resources
Add to theme header:
html
3. Optimize Custom Modules
Bad:jinja2
{% for i in range(1, 100) %}
{% module "card_" ~ i path="/modules/card" %}
{% endfor %}
Good:
jinja2
{% module "cards" path="/modules/card-grid" %}
4. Conditional Script Loading
Only load scripts when needed:
jinja2
{% if module.enable_slider %}
{% require_css %}
{% end_require_css %}
{% require_js %}
{% end_require_js %}
{% endif %}
Smart Content Strategies
1. Personalization by Lifecycle Stage
jinja2
{% if contact.lifecycle_stage == "lead" %}
Start your free trial today
{% elif contact.lifecycle_stage == "customer" %}
Unlock advanced features
{% else %}
Discover our solution
{% endif %}
2. Geographic Targeting
jinja2
{% set user_country = request.geoip.country_name %}
{% if user_country == "United Kingdom" %}
£99/month
{% elif user_country == "United States" %}
$99/month
{% else %}
€99/month
{% endif %}
3. Device-Specific Content
jinja2
{% if request.is_mobile %}
{% module "mobile_cta" path="/modules/mobile-cta" %}
{% else %}
{% module "desktop_cta" path="/modules/desktop-cta" %}
{% endif %}
4. Time-Based Content
jinja2
{% set current_hour = unixtimestamp()|datetimeformat('%H')|int %}
{% if current_hour >= 9 and current_hour < 17 %}
{% else %}
{% endif %}
Advanced Module Development
1. Complex Module Structure
modules/
advanced-card/
module.html
module.css
module.js
fields.json
meta.json
fields.json:
json
[
{
"id": "items",
"name": "items",
"label": "Card Items",
"type": "group",
"occurrence": {
"min": 1,
"max": 10,
"default": 3
},
"children": [
{
"id": "title",
"name": "title",
"label": "Title",
"type": "text"
}
]
}
]
2. Module with Custom Logic
module.html:jinja2
{% set items_per_row = module.layout == "grid" ? 3 : 1 %}
{% set total_items = module.items|length %}
{% set rows = (total_items / items_per_row)|round(0, 'ceil') %}
{% for item in module.items %}
{{ item.title }}
{{ item.description }}
{% if loop.index % items_per_row == 0 and not loop.last %}
{% endif %}
{% endfor %}
3. JavaScript-Enhanced Modules
module.js:javascript
class AdvancedCard {
constructor(element) {
this.element = element;
this.items = this.element.querySelectorAll('.card');
this.init();
}
init() {
this.setupEventListeners();
this.setupAnimations();
}
setupEventListeners() {
this.items.forEach(item => {
item.addEventListener('click', (e) => {
this.handleItemClick(e.currentTarget);
});
});
}
setupAnimations() {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animated');
}
});
},
{ threshold: 0.1 }
);
this.items.forEach(item => observer.observe(item));
}
handleItemClick(item) {
// Custom click handling
item.classList.toggle('expanded');
}
}
// Initialize on DOM ready
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.card-container').forEach(container => {
new AdvancedCard(container);
});
});
CLI Power User Tips
1. Watch Multiple Directories
bash
Watch and upload changes
hs watch --portal=12345 src/themes/custom
2. Create Custom Scripts
package.json:json
{
"scripts": {
"watch": "hs watch --portal=prod src",
"watch:dev": "hs watch --portal=dev src",
"upload": "hs upload --portal=prod src",
"fetch": "hs fetch --portal=prod --overwrite"
}
}
3. Environment-Specific Deploys
bash
Development
hs upload src themes --portal=dev
Production
hs upload src themes --portal=prod
4. Automated Testing
bash
Validate theme before upload
hs lint src/themes/custom
API Integration Techniques
1. Custom Module with API Data
jinja2
{% set api_data = hubdb_table_rows(123) %}
{% for row in api_data %}
{{ row.name }}
{{ row.description }}
{% endfor %}
2. Serverless Functions Integration
javascript
// Call HubSpot serverless function
fetch('/_hcms/api/function-name', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
data: formData
})
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
});
3. HubDB Dynamic Content
jinja2
{% set products = hubdb_table_rows(product_table_id) %}
{% set filtered = [] %}
{% for product in products %}
{% if product.category == module.selected_category %}
{% do filtered.append(product) %}
{% endif %}
{% endfor %}
{% for product in filtered|sort(attribute='price') %}
{'{{ product.name }}'}
{{ product.price }}
{% endfor %}
Advanced Form Techniques
1. Multi-Step Forms
javascript
class MultiStepForm {
constructor(formId) {
this.form = document.getElementById(formId);
this.steps = this.form.querySelectorAll('.form-step');
this.currentStep = 0;
this.init();
}
init() {
this.showStep(this.currentStep);
this.setupNavigation();
}
showStep(stepIndex) {
this.steps.forEach((step, index) => {
step.style.display = index === stepIndex ? 'block' : 'none';
});
}
nextStep() {
if (this.validateStep(this.currentStep)) {
this.currentStep++;
this.showStep(this.currentStep);
}
}
prevStep() {
this.currentStep--;
this.showStep(this.currentStep);
}
}
2. Progressive Profiling
Enable in form settings to show different fields to returning visitors.
3. Conditional Form Fields
javascript
// Show/hide fields based on selection
document.querySelector('[name="company_size"]').addEventListener('change', (e) => {
const enterpriseFields = document.querySelector('.enterprise-fields');
enterpriseFields.style.display = e.target.value === 'enterprise' ? 'block' : 'none';
});
Content Strategy Tips
1. Topic Clusters Setup
Pillar Page: "Ultimate HubSpot CMS Guide" Cluster Content:2. Internal Linking Automation
jinja2
{% set related_posts = blog_recent_posts('default', 3, blog_topic=content.topic_list[0]) %}
3. Content Upgrade Strategy
Offer lead magnets within content:
jinja2
{% if content.content_group_id == blog_id %}
{% module "content_upgrade" path="/modules/content-upgrade" %}
{% endif %}
Testing and QA
1. Automated Testing Checklist
2. A/B Testing Setup
Test Variations:3. User Testing
Tools:
Conclusion
These advanced techniques will help you:
With these tips and a great theme like Realize, you'll build exceptional HubSpot websites.