HubSpot Template Marketplace module requirements

Last updated:

Learn about the requirements to submit a module to the Template Marketplace. These requirements apply to both modules in a theme and independent modules. 

Module restrictions

Modules must not contain HubDB, calls to serverless functions, or the CRM object field.

The following module types should not be built as independent modules

  • HTML
  • Full-width modules
  • Forms and multi-step forms
  • Spacer modules or modules that create non-UI page structure
  • Modules that duplicate default module functionality
  • Commerce-specific modules
  • Email-specific modules

Module content

Learn about the requirements for module labels and help text, fields, and default content.

Module labels & help text

  • Modules must have descriptive labels that convey the purpose of the module. The label Hero Banner with Parallax Scrolling is descriptive, whereas the labels Hero Banner and Gallery are not. 
  • Module labels must not contain numbers, such as Hero Banner 01.
  • Module labels must not contain abbreviations, such as Col instead of Column.
  • Modules must contain inline help text where applicable to further convey how to use the module.
  • Module should not be named the same as a default module.

Default content

  • Default field cannot include Lorem ipsum text. 
  • Default field content should represent the field’s purpose:
    • When including menu fields, modules must use Select a menu as the default content option.
    • When including form fields, modules must use Select a form as the default content option.
    • When including blog selector fields, modules must use Select a blog as the default content option.
  • If adding default content to a module doesn't make sense, use a module placeholder instead to help the content creator visualize the space that they'll fill with content.

Module icons

Modules must include a custom icon assigned to the module (replacing the default icon). Do not use company logos as icons, such as Apple or Amazon logos. Learn more about module icons.

Module fields

Review specific requirements for modules in a theme and independent modules below
  • For modules in a theme:
    • Must contain inline help text and specific default content for certain fields.
    • A portion of the theme's color and logo must inherit from the account's brand settings.
      • At a minimum, three color fields must inherit colors from the account's brand settings. Additional color fields can default to other colors, including black and white.
      • At least one logo field must inherit from the account's brand settings. If using an image field to render a logo, the image field does not have to inherit from the brand settings. 
  • For both modules in a theme and independent modules:
    • Module field names should describe the field’s intent. For example, if a text field is meant to include a person’s job title, Job Title would be a proper description whereas Title would not.
    • All of HubSpot's default modules must be styled and must display properly on all templates submitted.

fields.json and module.html configuration

To ensure compatible functionality between themes and independent modules, modules must inherit the color and font fields either by defining default_value_path or property_value_paths, or both in their fields.json file and add a reference to the theme fields in the module.html file. Learn more about these requirements.

Module code quality

Modules must be self-contained

Theme modules

Any files needed for your theme module, such as CSS or JavaScript, must be contained in the theme folder and included in the theme directory. You can use the Linked Filed feature in the design manager. Alternatively, include the files using the require_js() or require_css() functions with a relative path to the file.

For common libraries, such as slick.js, you can include them using the require_js() or require_css() functions with an absolute URL to the CDN where it's hosted. 

Please note: do not use absolute URLs to assets contained within your development portal as cross-portal references will not resolve. 

Independent modules

For independent modules, all CSS and Javascript files should be contained in either the module.css or module.js. Alternatively, include the files using the require_js() or require_css() functions with an absolute URL to the CDN where it’s hosted. It is not possible to use the Linked Files feature in the Design Manager as that is only available for theme modules. 

Since module.js is included in the DOM before any require_js or require_css files, Javascript contained in the module.js section should be deferred using the annotation below:

JavaScript
document.addEventListener("DOMContentLoaded", function(){
// Put Javascript here
});

All scripts and files should be rendered in the head of the module's HTML

Code restrictions for independent modules

The following restrictions apply to only independent modules:

  • It is recommended to use vanilla JS where possible. Adding a jQuery library to a site that is not using jQuery can potentially cause conflicts and slow down the website page.
  • If using a jQuery library, use the require_js() function to include the library in the event that jQuery is disabled with the checkbox (Boolean) in portal settings to avoid conflicts from multiple jQuery libraries. 
{% if not site_settings.include_jquery %} {{ require_js("https://code.jquery.com/jquery-3.7.0.min.js", "footer") }} {% endif %}

Categories

  • All independent modules must have at least one category. Modules submitted as part of a theme are not required to have categories, but it's best practice to include at least one. Learn more about adding categories to modules

Class name selectors

  • Any class name selectors must be prefixed with the module name, replacing spaces with hyphens. For example, below is the module.html file for a button named example-button, with each class name and CSS selector reflecting its name.
<style> {% scope_css %} {# Button wrapper #} {% if module.styles.group_alignment.alignment %} .example-button-wrapper { text-align: {{ module.styles.group_alignment.alignment.horizontal_align }}; } {% endif %} {# Button #} .example-button { {% if module.styles.group_background.color.color %} background-color: rgba({{ module.styles.group_background.color.color|convert_rgb }}, {{ module.styles.group_background.color.opacity / 100 }}); {% endif %} } {% end_scope_css %} </style> {% end_require_css %} {##### Module HTML #####} <div class="example-button-wrapper"> <a href="{{ href }}" class="example-button" {% if module.button_link.open_in_new_tab %}target="_blank"{% endif %} {% if rel %}rel="{{ rel|join(" ") }}"{% endif %} > {{ module.button_text }} </a> </div>

Styles and Javascript

  • Styles:
    • Modules must have a non-empty style group.
    • Hardcoding inline styles within modules is not recommended. Instead, use dynamic inline styles by enabling fields to control styling.
  • JavaScript:
    • JavaScript must be able to represent multiple instances of a module. JavaScript in the JS Pane will only load once per page, regardless of the number of module occurrences.
    • JavaScript should reference DOM elements by module-specific class names to ensure elements outside of the module are not unintentionally affected.JavaScript:
When creating modules, you can use a built-in variable called Template Marketplace | Module requirements. This variable pulls in the module's instance ID (which can be used in the HTML+HubL panel only) to help in CSS and JS markup for complex modules. Learn more about this in our developer documentation.

Field organization

The following field organization and grouping requirements must be met.

Content tab

  • Where there is at least one control within a field group, all controls must be grouped into categories labeled by their function.
  • Module fields added to the Content tab must provide ways to customize the content of a module. For example, controls for image, icon, alt-text, and link controls.

Styles tab

Module style field groups must be consistent and follow a pattern. Below is a recommended order for your style field groups. These groups can either be at the top level or nested one group deep. Empty groups may also be removed:
  • Presets
  • Text
  • Background
  • Border
  • Hover
  • Corner
  • Spacing
  • Alignment
  • Custom style groups that don't fit the above
  • Advanced

The following field types must be contained in the Styles tab if present:

When moving fields from the Content tab to the Styles tab, learn how to use alias mapping to preserve styling for modules that are already in use on live pages.
  • Animation options should always be positioned near the bottom of the field group list.
  • Options that allow content creators to add code snippets or CSS should be grouped at the end of the field group list under a field labeled Advanced
  • Controls should be standardized across all modules. For example, all elements that can have a border radius control should offer that control. Avoid offering controls on some modules that are absent on others.
  • Module fields added to the Style tab must provide ways to style the module. For example:
    • Style options such as color, text styling, alignment, spacing, border, and corner radius.
    • Animations such as hover and slide-in effects.
    • Presets such as dark and light themes that are meant to change many styles at the same time.

Examples of field organization

Presets

Presets can be used when wanting to give content creators a limited set of options, often tying back to theme settings. For example, the Icon module included in the Growth theme contains presents for Dark and Light colors, which enables consistency when used across the website. 

Multi-level grouping

When deciding whether to keep style fields at the top level or nest them, consider the following example.

The Icon module included in the Growth theme lists all its styles at the top level because it's one component, and therefore its style options all impact the one component. 

growth-theme-icon-module

The Speaker card module included in the Growth theme contains multiple components: the card's image and its text contents. Module styles are therefore grouped by component so that the content creator has a more clear process for styling each component.

growth-theme-speaker-card

Grouping individual fields

The button module below contains groupings for Presets, Text, Background, and more. Although the Corner field group contains only the corner radius control, it’s still grouped to create a uniform content creation experience.

module-requirements-2_1button-styles

 

Aliases

Alias mapping enables you to create field mappings in a module so that you can move, rename, or replace its fields without impacting pages that are using the module. 

For example, a module is being used on a live page. You want to move some fields into the Styles tab, such as color or font, but a content creator has already selected values for those fields in the editor. If you were to move those fields without setting up alias mapping, HubSpot would not be able to relocate those fields and they would revert to their default values, which would undo the styling on the live page.

Instead, you can add an aliases_mapping property to a field to map it to another one. Then, when a value has not been set for the original field, HubSpot will check if a value exists in the mapped field. If no value exists in the mapped field either, it will use the default value instead. This property can be used to map field values between different versions of a module only when the stored data type of the old field is the same as the new field's stored data type.

For a visual walkthrough of this feature, check out video below.

To migrate existing fields to aliases:

  1. Create new fields and map them to old fields using the aliases_mapping property.
  2. Remove the old field definition.
  3. Update the module.html file to use the new fields definition.

Please note:

  • You cannot map fields that are of a different data type to each other. For example, you can't map a background gradient field to an image field. The stored value has to be a valid value for the new field's type.
  • When creating a new field with an alias mapping to an old field, the default values and required properties of both fields should be the same.

Below are examples of implementing this for both simple and complex changes:

Simple implementation

In simple situations, the field type of the old field and the field type of the new field should be the same. For example:

  • Old color field to new color field. 
  • Old text field to new text field.
  • Old spacing field to new spacing field.

Below is an example of using aliases_mapping when moving a color field from the module's Content tab to the Styles tab.

Example of a v0 module

[ { "label": "Button Color", "name": "old_button_color_field", "type": "color", "required": true, "default": { "color": "#FFFFFF", "opacity": 100 } } ]

Example of a v1 module

[ { "label": "Styles", "name": "styles", "type": "group", "tab": "STYLE", "children": [ { "label": "Button Color", "name": "new_button_color_field", "type": "color", "required": true, "aliases_mapping": { "property_aliases_paths": { "new_button_color_field": ["old_button_color_field"] } }, "default": { "color": "#FFFFFF", "opacity": 100 } } ] } ]

Complex implementation

In more complex situations, you can also map fields to subfields or other module field types as long as the data type is the same, and the new field's subfield type matches. Subfields are the properties within the field's stored value object. For example:

  • Mapping a Rich text field to a Text field, as the values in both fields are stored as strings.
  • Consolidating typography fields, such as changing from a number field for font size, to use a font field (which has a font size sub field). You can add an alias for the size subfield to map it to the old number field by using dot notation.

Below is an example of changing the font sizing option from a number field to a font field which has a font size sub field.

Example of a v0 module

[ { "name": "my_number_field", "label": "Number field", "required": false, "locked": false, "display": "text", "step": 1, "type": "number", "min": null, "max": null, "inline_help_text": "", "help_text": "", "default": null } ]

Example of a v1 module

[ { "name": "my_font_field", "label": "font_field", "required": false, "locked": false, "inline_help_text": "", "help_text": "", "load_external_fonts": true, "type": "font", "aliases_mapping": { "property_aliases_paths": { "my_font_field.size": ["my_number_field"] } }, "default": { "size": 12, "font": "Merriweather", "font_set": "GOOGLE", "size_unit": "px", "color": "#000", "styles": {} } } ]

Was this article helpful?
This form is used for documentation feedback only. Learn how to get help with HubSpot.