HH Components Reference Guide

Modified on Wed, 28 Jan at 2:34 PM

HH Components Reference Guide

Hubhus hh- components are powerful tools for dynamically fetching and displaying data in your campaigns. This comprehensive reference guide provides copy-paste examples organized by use case, making it easy to implement dynamic features in your emails, forms, and pages.

 Try our Interactive Component Generator → 

Build component code visually with our web-based generator tool
Note: This is a community tool, not an official Hubhus feature. Contact support@hubhus.com for assistance.

On this page

What are HH components?

HH components are custom HTML elements that start with hh- and fetch or process data from your Hubhus campaign. They work like placeholders but provide much more powerful functionality:

  • Data fetching – Pull calendar events, leads, services, resources, and more
  • Data processing – Count items, format dates, filter results
  • Conditional logic – Show/hide content based on data availability
  • Iteration – Loop through data collections
  • Integrations – Embed maps, external services, and APIs

How to use this guide

Each section below contains ready-to-use code examples. Simply copy the code, replace the placeholders (marked with YOUR_VALUE_HERE), and paste into your template.


Calendar Events

Fetch and display calendar events with full control over filtering, sorting, and included relations.


Fetch all upcoming events

<hh-for each="event" in='<hh-data.calendar-events 
  where="start>@now" 
  sort="start" 
  limit="50" />'>
  <div>
    <strong>{event.title}</strong><br>
    Date: {event.start}<br>
    Location: {event.location}
  </div>
</hh-for>

Parameters explained:

  • where="start>@now" – Only events starting after current date/time
  • sort="start" – Sort by start date (ascending)
  • limit="50" – Maximum 50 results

Fetch events in a specific date range

<hh-for each="event" in='<hh-data.calendar-events 
  filter="start > @str2datetime[2025-12-01 00:00]" 
  filter="start < @str2datetime[2025-12-31 23:59]" 
  sort="start" 
  limit="500" />'>
  Event: {event.title} on {event.start}
</hh-for>

Use case: Display all events in December 2025.

Important

For date ranges, use filter with @str2datetime instead of where to avoid date parsing issues.


Fetch events with resource information

<hh-for each="event" in='<hh-data.calendar-events 
  include="calendar_resources.user" 
  where="start>@now" 
  sort="start" 
  limit="50" />'>
  <div>
    <strong>{event.title}</strong><br>
    Assigned to: <hh-for each='res' in='event.calendar_resources' glue=", ">{res.user.name}</hh-for>
  </div>
</hh-for>

What this does: Shows each event with the names of all assigned resources, separated by commas.


Fetch events with service information

<hh-for each="event" in='<hh-data.calendar-events 
  include="calendar_services" 
  where="start>@now" 
  sort="start" 
  limit="50" />'>
  <div>
    <strong>{event.title}</strong><br>
    Service: <hh-for each='srv' in='event.calendar_services' glue=", ">{srv.name}</hh-for>
  </div>
</hh-for>

Count total events

You have <hh-data.count><hh-data.calendar-events where="start>@now" /></hh-data.count> upcoming events.

Result: "You have 15 upcoming events."


Leads

Fetch and display lead information from your campaigns.


Fetch recent leads

<hh-for each="lead" in='<hh-data.leads 
  sort="created_at" 
  limit="10" />'>
  <div>
    <strong>{lead.name}</strong><br>
    Email: {lead.email}<br>
    Phone: {lead.phone}<br>
    Created: {lead.created}
  </div>
</hh-for>

Note: Use sort="created_at" to sort by creation date (ascending). For descending (newest first), check if your system supports sort="-created_at" or use a different field.


Filter leads by status

<hh-for each="lead" in='<hh-data.leads 
  filter-status="slug = moede-booket" 
  sort="created_at" 
  limit="50" />'>
  {lead.name} - {lead.email}
</hh-for>

Replace "moede-booket" with your actual status slug. Important: Note the space around the equals sign: "slug = value" not "slug=value".


Filter leads by custom field

<hh-for each="lead" in='<hh-data.leads 
  filter-field_value="zipcode LIKE 20*" 
  sort="created_at" 
  limit="50" />'>
  {lead.name} - {lead.email}
</hh-for>

Use case: Filter by custom field values using filter-field_value instead of where.


Count leads

Total leads: <hh-data.count><hh-data.leads /></hh-data.count>
Active leads: <hh-data.count><hh-data.leads filter-status="slug = active" /></hh-data.count>

Booking Availability

Check available booking times and control booking form behavior.


Check if booking slots are available

@if(<hh-data.count><hh-booking.available-dates 
  booking-form="YOUR_BOOKING_FORM_ID" 
  full-address="%lead_full_address%" 
  latest-date="+14days" /></hh-data.count> > 0){
  Booking slots are available!
}@else{
  No slots available in the next 14 days.
}@endif

Parameters:

  • booking-form="YOUR_BOOKING_FORM_ID" – Replace with your booking form ID
  • full-address="%lead_full_address%" – The address to check availability for
  • latest-date="+14days" – Check availability up to 14 days ahead

Get optimal booking time slots

<hh-for each="slot" in='<hh-booking.optimal-datetime-slots 
  booking-form="YOUR_BOOKING_FORM_ID" 
  full-address="%lead_full_address%" 
  earliest-date="@now" 
  latest-date="+30days" 
  limit="5" />'>
  Available: {slot.datetime} ({slot.score}% optimal)
</hh-for>

Use case: Show the top 5 most optimal booking times based on resource availability and efficiency.


Use in automation conditions

@if(<hh-data.count><hh-booking.available-dates 
  latest-date="+10days" 
  booking-form="YOUR_BOOKING_FORM_ID" 
  full-address="%lead_full_address%" /></hh-data.count> > 1){1}@else{0}@endif

Result: Returns 1 (true) if more than one slot is available, otherwise 0 (false). Perfect for automation triggers.


Calendar Resources

Fetch resource information, groups, and tags for dynamic resource selection.


List all resource groups

<hh-data.calendar-resource-groups pluck="name,id" />

Output example:

{
  "176": "Installation Team",
  "177": "Installation Team - North",
  "178": "Installation Team - South",
  "179": "Service Team - East"
}

Use case: Programmatically fetch all resource tag names and IDs for dynamic resource selection in booking forms.


List all calendar services

<hh-data.calendar-services pluck="name,id" limit="200" />

Result: JSON object with all service names and IDs.


Date Formatting

Format dates for display or calculations.


Format a date

<hh-date.format input="%event_start_datetime_db%" output="d/m/Y" />

Result: "15/12/2025"


Calculate a date (add/subtract days)

<hh-date.format input="%event_start_datetime_db% -1day" output="Y-m-d" />

Result: Date one day before the event start (e.g., "2025-12-14")

Common calculations:

  • +7days – Add 7 days
  • -3days – Subtract 3 days
  • +2weeks – Add 2 weeks
  • -1month – Subtract 1 month

Important

Always use the _db variant of date placeholders (e.g., %event_start_datetime_db%) when doing date calculations. Also ensure there is a space before the calculation: %date% -1day not %date%-1day.


Use in hidden form inputs

<input name='latest_date' 
  value='<hh-date.format input="%event_start_datetime_db% -1day" output="Y-m-d" />' 
  type='hidden' />

Use case: Limit booking form date picker to show only dates before the current event.


Data Fields

Read and display custom data field values (JSON structured data).


Read a data field value

<hh-for each='item' in='<hh-data-fields.value slug="YOUR_DATA_FIELD_SLUG" />'>
  <div>
    Name: {item.name}<br>
    Value: {item.value}<br>
    Quantity: {item.quantity}
  </div>
</hh-for>

Replace YOUR_DATA_FIELD_SLUG with your data field's slug (API name).

Use case: Display line items from an invoice, checklist items, product selections, or any structured JSON data stored in a data field.


Maps Integration

Embed Google Maps features directly into your templates.


Embed Google Street View

<div style='max-width:100%; min-height: 400px;'>
  <hh-gmap.street-view 
    location="%lead_full_address%" 
    style='min-height: 500px;' />
</div>

Use case: Show property street view in lead profile pages, booking confirmations, or inspection reports.


Counting and Data Validation

Count results from any data source for conditional logic.


General counting syntax

<hh-data.count>
  <hh-data.COMPONENT_NAME [parameters] />
</hh-data.count>

Wraps any hh-data.* component to return the count instead of the data.


Count examples

<!-- Count upcoming events -->
<hh-data.count><hh-data.calendar-events where="start>@now" /></hh-data.count>

<!-- Count available booking slots -->
<hh-data.count><hh-booking.available-dates booking-form="123" full-address="%lead_full_address%" /></hh-data.count>

<!-- Count leads with specific status -->
<hh-data.count><hh-data.leads filter-status="slug = qualified" /></hh-data.count>

Counting Microsoft Calendar Events

Count Microsoft 365 calendar events with advanced filtering options for resources, time ranges, and combinations.

Filter by specific resource (by ID):

<hh-data.microsoft-events.count filter-calendar_resources="id = 1790" />

Filter by resource email:

<hh-data.microsoft-events.count filter-calendar_resources="email = john@company.com" />

Time interval - This week:

<hh-data.microsoft-events.count 
  filter="start > @str2datetime[monday this week]" 
  filter="start < @str2datetime[sunday this week]" />

Time interval - Next week:

<hh-data.microsoft-events.count
  filter="start > @str2datetime[monday next week]" 
  filter="start < @str2datetime[sunday next week 23:59]" />

Combined filters - Specific resource this week:

<hh-data.microsoft-events.count 
  filter-calendar_resources="id = 1790"
  filter="start > @str2datetime[monday this week]" 
  filter="start < @str2datetime[sunday this week 23:59]" />

Advanced - Count by resource tag/group:

<hh-calculator precision="0">0<hh-for each="resource" in='<hh-data.calendar-resource-groups.first filter="id=140" include="calendar_resources" get="calendar_resources" />'>+<hh-data.microsoft-events.count filter="start > @str2datetime[monday this week]" filter="start < @str2datetime[sunday this week 23:59]" filter-calendar_resources="id = {resource.id}" /></hh-for></hh-calculator>

Finding resource tag IDs

To find all resource tag IDs for use in filters, use:
<hh-data.calendar-resource-groups output="html-table-style-one" />


Common Parameters

These parameters work across multiple components:


Filtering (where vs filter)

For simple properties: Use where

where="field_name=value"
where="field_name>value"
where="field_name<value"
where="field_name!=value"

For date ranges: Use filter with @str2datetime

filter="start > @str2datetime[today]"
filter="start < @str2datetime[tomorrow]"

For custom field filtering (leads only):

filter-field_value="zipcode LIKE 20*"
filter-status="slug = won"

Pro tip

Use filter instead of where for date ranges to avoid parsing errors. You can use multiple filter or where attributes together.


Sorting (sort)

sort="field_name"        <!-- Ascending (A-Z, oldest-newest) -->
sort="-field_name"       <!-- Descending (Z-A, newest-oldest) -->

Examples:

  • sort="start" – Sort calendar events by start date (earliest first)
  • sort="-start" – Sort calendar events by start date (newest first)
  • sort="created_at" – Sort leads by creation date (oldest first)
  • sort="name" – Sort alphabetically by name

Important

For leads, use sort="created_at" (not "created"). For calendar events, use sort="start". Different data types may use different field names.


Limiting results (limit)

limit="10"              <!-- Return maximum 10 results -->

include="calendar_resources.user"        <!-- Include resource with user info -->
include="calendar_services"              <!-- Include service info -->
include="calendar_resources,calendar_services"  <!-- Multiple includes -->

Plucking specific fields (pluck)

pluck="name"                <!-- Return only name field -->
pluck="name,id"             <!-- Return name and id fields -->

Use case: Reduce data size and output only needed fields.


Loop Syntax (hh-for)

Iterate through data collections returned by components.


Basic loop structure

<hh-for each="ITEM_NAME" in='<hh-data.COMPONENT [params] />'>
  {ITEM_NAME.field_name}
</hh-for>

Components:

  • each="ITEM_NAME" – Variable name for each item (choose any name)
  • in='...' – The component that returns data
  • {ITEM_NAME.field} – Access item properties inside the loop

Loop with separator (glue)

<hh-for each='res' in='event.calendar_resources' glue=", ">
  {res.user.name}
</hh-for>

Result: "John Smith, Sarah Johnson, Mike Davis"

Common separators:

  • glue=", " – Comma and space
  • glue=" | " – Pipe separator
  • glue="<br>" – Line breaks

Nested loops

<hh-for each="event" in='<hh-data.calendar-events include="calendar_resources.user" limit="10" />'>
  <div>
    <strong>{event.title}</strong><br>
    Resources:
    <ul>
      <hh-for each='res' in='event.calendar_resources'>
        <li>{res.user.name} ({res.user.email})</li>
      </hh-for>
    </ul>
  </div>
</hh-for>

Practical Real-World Examples

Complete examples ready to use in production.


Email notification with availability check

<p>Hi %lead_firstname%,</p>

@if(<hh-data.count><hh-booking.available-dates 
  booking-form="YOUR_FORM_ID" 
  full-address="%lead_full_address%" 
  latest-date="+7days" /></hh-data.count> > 0){
  <p>Good news! We have available times this week:</p>
  <p><a href="@bookingFormUrlLead[YOUR_FORM_NAME]">Click here to book</a></p>
}@else{
  <p>Unfortunately, we're fully booked this week. We'll contact you when new times become available.</p>
}@endif

Lead dashboard with counts

<div style="background: #f3f2f1; padding: 20px; border-radius: 8px;">
  <h2>Campaign Dashboard</h2>
  
  <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px;">
    <div style="background: white; padding: 15px; border-radius: 4px;">
      <h3><hh-data.count><hh-data.leads /></hh-data.count></h3>
      <p>Total Leads</p>
    </div>
    
    <div style="background: white; padding: 15px; border-radius: 4px;">
      <h3><hh-data.count><hh-data.calendar-events where="start>@now" /></hh-data.count></h3>
      <p>Upcoming Events</p>
    </div>
    
    <div style="background: white; padding: 15px; border-radius: 4px;">
      <h3><hh-data.count><hh-data.leads filter-status="slug = interested" /></hh-data.count></h3>
      <p>Interested Leads</p>
    </div>
  </div>
</div>

Booking form with conditional availability

<div class="form-group">
  <h3>Book Your Appointment</h3>
  
  @if(%event_datetime%){
    @if(1 > <hh-data.count><hh-booking.available-dates 
      latest-date="+21days" 
      booking-form="YOUR_FORM_ID" 
      full-address="%lead_full_address%" /></hh-data.count>){
      <p>You have a booking on <b>%event_datetime%</b></p>
      <p>You already have the earliest available time. We look forward to seeing you!</p>
    }@else{
      <p>You have a booking on <b>%event_datetime%</b></p>
      <p>We can offer you an earlier time if you'd like. Select a date and time below.</p>
    }@endif
  }@else{
    <p>Select your preferred date and time:</p>
  }@endif
</div>

Component Shortcuts

Most data components have shortcut variants for common operations:


Direct count variants

Instead of wrapping with <hh-data.count>, use direct count variants:

<!-- Standard way -->
<hh-data.count><hh-data.calendar-events where="start>@now" /></hh-data.count>

<!-- Shortcut way -->
<hh-data.calendar-events.count where="start>@now" />

Available shortcuts:

  • hh-data.calendar-events.count
  • hh-data.leads.count
  • hh-data.calendar-services.count
  • hh-data.calendar-resource-groups.count

Get first item variants

Get the first item from a collection without looping:

<!-- Get first upcoming event -->
@with(event = <hh-data.calendar-events.first where="start>@now" />){
  Next event: {event.title} on {event.start}
}@endwith

<!-- Get first lead -->
@with(lead = <hh-data.leads.first sort="created_at" />){
  Latest lead: {lead.name}
}@endwith

Available shortcuts:

  • hh-data.calendar-events.first
  • hh-data.leads.first
  • hh-data.calendar-services.first
  • hh-data.calendar-resource-groups.first

Troubleshooting

Component not rendering

If a component doesn't display data:

  • Check that all required parameters are provided
  • Verify parameter values are correct (IDs, slugs, field names)
  • Ensure placeholders inside parameters are valid (e.g., %lead_full_address%)
  • Check for typos in component names (hh-data.calendar-events, not hh-calendar-events)
  • Test with simple examples first, then add complexity

Date range filtering errors

Common mistake: Using where for date ranges

Wrong

where="start>2025-12-01" where="start<2025-12-31"

Correct

filter="start > @str2datetime[2025-12-01]" 
filter="start < @str2datetime[2025-12-31]"

Date calculation errors

Common mistakes:

Wrong

  • input="%date%-1day" - Missing space before calculation
  • input="%event_start_datetime% -1day" - Should use _db variant

Correct

input="%event_start_datetime_db% -1day"

Custom field filtering errors

Common mistake: Using where for custom fields

Wrong

where="zipcode=2000"

Correct

filter-field_value="zipcode = 2000"

Empty results or zero count

If components return no data:

  • Verify data exists that matches your filter conditions
  • Use filter with @str2datetime for date ranges
  • Remove filters temporarily to see all results
  • Check that the lead/event has the required related data

Syntax errors

Common syntax mistakes:

  • Missing closing tags: Ensure </hh-for> and </hh-data.count> are present
  • Incorrect quotes: Use single quotes inside in='...' attributes
  • Unescaped special characters: Use &lt; instead of < in HTML context
  • Missing self-closing slash: Components must end with />

Best Practices

Performance

  • Always use limit parameter to restrict result counts
  • Use where filters to reduce data volume
  • Only include related data when needed
  • Use pluck to return only required fields

Maintainability

  • Add comments to complex logic: <!-- Check availability for next 14 days -->
  • Use descriptive variable names in loops: each="event" not each="e"
  • Break complex nested structures into separate components
  • Document parameter values for future reference

Testing

  • Test with multiple leads to ensure data variations are handled
  • Test with empty data sets to verify fallback logic
  • Test in different contexts (emails, forms, pages)
  • Preview emails before sending to production

Summary

HH components provide powerful data fetching and processing capabilities for Hubhus campaigns. Use hh-data.calendar-events to fetch calendar events with filtering, sorting, and related data (resources, services). Use hh-booking.available-dates and hh-booking.optimal-datetime-slots to check booking availability and display optimal times. Use hh-data.count to wrap any component and return the count instead of data—perfect for conditional logic in automations. Use hh-date.format to format and calculate dates with operations like +7days or -1month. Use hh-data-fields.value to read structured JSON data from custom data fields. Use hh-gmap.street-view to embed Google Street View for addresses. Use hh-for to loop through data collections with optional separators using glue. Common parameters include where for filtering, sort for ordering (use -field for descending), limit for restricting results, include for loading related data, and pluck for selecting specific fields. Always use limits to maintain performance, add comments for complex logic, and test with various data scenarios. Components can be nested, combined with conditional logic, and used across emails, forms, pages, and automation templates.

Common searches

hh components • dynamic data • fetch calendar events • check booking availability • loop through data • data fields • count results • date formatting • calendar resources • placeholders advanced

Also known as

hubhus components • dynamic components • data components • template components • hh tags • custom placeholders • dynamic content • API components

Was this article helpful?

That’s Great!

Thank you for your feedback

Sorry! We couldn't be helpful

Thank you for your feedback

Let us know how can we improve this article!

Select at least one of the reasons
CAPTCHA verification is required.

Feedback sent

We appreciate your effort and will try to fix the article