Module and theme fields overview

Last updated:

Modules and Themes have the concept of fields. Fields are form fields for customizing the module or theme. Fields can be used to control both style and function for modules and themes used on a site. Below, learn more about how to create and manage options for module and theme fields.

To learn more about specific field types, check out the module and field types reference guide

Creating and managing fields

You can add fields to a module's fields.json file locally through the HubSpot CLI and in the in-app module editor. To add fields to a theme, you must update the theme's fields.json file locally using the CLI. 

HubSpot CLI

When building locally module and theme fields can be edited through a fields.json file inside of the module or theme's folder. For modules, this file will automatically be created when using the hs create module command. All of the field options available in the module editor are available as properties you can add or edit in the fields.json file. This includes repeater fields, groups, and conditions. One of the benefits of editing locally is that it makes it easier to include your modules in version control systems like git.

Module editor

The design manager has a built-in module editor UI that enables you to create, group, and edit module fields. The module editor contains a module preview which enables you to see what the module looks like on its own, as well as test your fields. Since modules do not live in a vacuum you should always test them on a template you plan to use, to see what template level styles may affect it. Be aware if a module is contained in a locked folder it cannot be edited this way.

Design Manager Module Editor

Please note: if you are working mostly locally but want to use the module editor to configure fields, make sure to fetch your changes. This is especially important for those using version control systems like git.

Side by side fields

By default, module fields in content editors stack vertically. However, you can place module fields side by side by adding a display_width property to fields in the fields.json file with a value of half_width


A single field with a display_width of half_width will appear as half-width in the content editor. When the field above or below that field in the fields.json file is set to half_width, they'll be placed side by side.

// Example module fields.json file [ { "name": "number_field", "label": "Number", "required": false, "locked": false, "display": "slider", "min": 1, "max": 10, "step": 1, "type": "number", "prefix": "", "suffix": "", "default": null, "display_width":"half_width" }, { "label": "Description", "name": "description", "type": "text", "required": true, "default": "Add a description", "display_width": "half_width" } ]

Field groups

When fields are related to each other often it makes sense for them to be displayed visually grouped. Modules and Themes support grouping multiple fields together. 

Field group without nested field groups

Field groups without nested field groups display simply with dividers above and below the group, and the group's label is displayed at the top of the group.

Nested field group

Field Groups can be nested. A field group that contains another field group will display as a button. Clicking the button to view the group will show the contents of that group.

Field groups can be nested 3 levels deep, meaning module fields, can have 4 levels of depth. Making it easy to create user interfaces that convey field relationships and more granular depth.

Field groups in fields.json

Field group objects can be listed as children of other field groups, their structure is very similar to field's themselves with the only special parameter being the "children" parameter, which is an array of fields and groups they contain.

// Field group example { "type": "group", "name": "typography", "label": "Typography", "expanded": true, "children": [ { "type": "font", "name": "h1_font", "label": "Heading 1", "load_external_fonts": true, "default": { "color": "#000", "font": "Merriweather", "font_set": "GOOGLE", "variant": "700", "size": "48" } } ] } // Field group inside of a field group { "type": "group", "name": "header", "label": "Header", "children": [ { "type": "font", "name": "h1_font", "label": "Heading 1", "load_external_fonts": true, "default": { "color": "#000", "font": "Merriweather", "font_set": "GOOGLE", "variant": "700", "size": "48" } { "type": "group", "name": "navigation", "label": "Navigation", "expanded": false, "children": [ { "name" : "bg_color", "label" : "Background color", "sortable" : false, "required" : false, "locked" : false, "type" : "color", "default" : { "color" : "#ff0000", "opacity" : 100 } } ] } } ] }

Expanding field groups by default

Field groups can be set to be expanded by default by setting the expanded boolean property to true in the fields.json group properties as shown in the example code above. Field groups are not expanded by default and when using nested field groups, the parent group cannot make use of this property.

Outputting field values within Field Groups

Field groups create dicts that contain the field values you want to output. If you nest field groups the nested field group is a dict inside of the outside field group dict. To access that data you will traverse the tree from either the root theme or module variable depending on your context.

<div> {# printing a value from a field group `recipe_summary` is the field group, `title` is the text field. #} {{module.recipe_summary.title}} </div>/* Printing a Font field's color value, when the font field is within a field group. `typography` is the field group, `h1_font` is the field */ h1{ color: {{ theme.typography.h1_font.color }}; }

Style fields

Style fields are a special field group type in a module or theme's fields.json file that give content creators control over a module or theme's styling in the page and theme editor. Below, learn how to add style fields to a module or theme. Learn about best practices for using and organizing style fields.

Module style fields

Style fields added to a module will appear on the Styles tab of the page editor when editing the module: 


When adding style fields to a module's fields.json file, you add them within one styles group. That group, however, can contain multiple groups within it, as shown below:

// Module style fields [ { "type": "group", "name": "styles", "tab": "STYLE", "children": [{ "name": "img_spacing", "label": "Spacing around image", "required": false, "type": "spacing", "default": { "padding": { "top": { "value": 10, "units": "px" }, "bottom": { "value": 10, "units": "px" }, "left": { "value": 10, "units": "px" }, "right": { "value": 10, "units": "px" } }, "margin": { "top": { "value": 10, "units": "px" }, "bottom": { "value": 10, "units": "px" } } } }] } ]

The following fields can be used as style fields in modules. Learn about each of the field types in the module and field types guide.

Learn more about module and theme field types.

View the CMS boilerplate for an example of a style fields within a module's fields.json file.

Theme style fields

Style fields added to a theme will appear in the left sidebar of the theme editor:


All style fields within a theme's fields.json file will be added to the left sidebar of the theme editor, as opposed to needing to put them under a styles group, as shown below:

// Theme style fields [ { "label": "Global colors", "name": "global_colors", "type": "group", "children": [ { "label": "Primary", "name": "primary", "type": "color", "visibility": { "hidden_subfields": { "opacity": true } }, "default": { "color": "#494A52" } }, { "label": "Secondary", "name": "secondary", "type": "color", "visibility": { "hidden_subfields": { "opacity": true } }, "default": { "color": "#F8FAFC" } } ] }, { "label": "Global fonts", "name": "global_fonts", "type": "group", "children": [ { "label": "Primary", "name": "primary", "type": "font", "visibility": { "hidden_subfields": { "size": true, "styles": true } }, "inherited_value": { "property_value_paths": { "color": "theme.global_colors.primary.color" } }, "default": { "fallback": "sans-serif", "font": "Lato", "font_set": "GOOGLE" } }, { "label": "Secondary", "name": "secondary", "type": "font", "visibility": { "hidden_subfields": { "size": true, "styles": true } }, "inherited_value": { "property_value_paths": { "color": "theme.global_colors.primary.color" } }, "default": { "fallback": "serif", "font": "Merriweather", "font_set": "GOOGLE" } } ] }, { "name": "branding_color", "label": "branding_color", "type": "color", "default": { "color": "#3b7bc0", "opacity": 60 }, "inherited_value": { "property_value_paths": { "color": "brand_settings.primaryColor" } } }, { "name": "secondary_branding_color", "label": "Secondary Branding color", "type": "color", "default": { "color": "#ff6b6b", "opacity": 60 }, "inherited_value": { "property_value_paths": { "color": "brand_settings.colors[2]" } } } ] } ]

The following fields can be used as style fields in modules. Learn about each of the field types in the module and field types guide.

Learn more about module and theme field types.

View the CMS boilerplate for an example of a style fields within a theme's fields.json file. 

Please note: if you're a marketplace provider, you should not replace existing content fields with style fields in existing modules. Changing the hierarchy of fields in a fields.json file can result in existing module instances losing their data. Instead, you should add new style fields, or create a new listing that has the fields appropriately grouped. This will prevent your updates from being breaking changes for customers relying on your themes. To advocate for migration paths for old modules, check out the HubSpot Ideas forum.

Generated CSS

Some style fields provide a way to output css directly based on the field's value. This is especially helpful with fields that can control more complicated styling like gradients. The following style fields have a generated .css property:

{% require_css %} <style> {% scope_css %} .team-member { {% if %} background: {{ }}; {% endif %} {{ }} {{ }} {{ }} } {% end_scope_css %} </style> {% end_require_css %}


When creating modules that format information, often there are types of information that repeat. A recipe module for example, might have a field for "Ingredient". Well, most recipes have more than 1 ingredient. You could give them a rich text field, but then you lose your ability to force consistent styling and add functionality around each ingredient. That's where repeaters come in, HubSpot has two forms of repeaters: Repeating fields, and Repeating groups.

Repeating fields

Repeating fields are normal fields but content creators can add, remove, and re-arrange instances of the field. Using the recipe module example above, each ingredient could be a repeating text field. 

repeater field

This makes it so the content creator can add as many ingredients as they wish. From the developer perspective, you get an array that you can loop through to print out that list of ingredients, applying the formatting and functionality you want. 

Repeating fields are best used for very simple situations. Often times repeating groups make more sense.

Please note: it's not currently possible to set the default order of repeating fields.

Repeating fields in fields.json

// Repeating field example { "name" : "ingredient", "label" : "Ingredient", "required" : false, "locked" : false, "occurrence" : { "min" : 1, "max" : null, "sorting_label_field" : null, "default" : 1 }, "allow_new_line" : false, "show_emoji_picker" : true, "type" : "text", "default" : [ "1 cup water" ] }

Loop through items in module HTML+HubL

<!--Looping through a repeating field--> <ul> {% for item in module.ingredient %} <li>{{ item }}</li> {% endfor %} </ul>

Repeating groups

Repeating groups are field groups with the repeating option enabled. Repeating groups allow content creators to add, remove, and re-arrange groups of fields. Using the recipe module example, say that you want to integrate your ingredients list with a shopping list functionality.

Repeating group of fields

The quantity of an ingredient would be critical to the shopping list. While someone could provide that in the text field, the module would then need to parse the text field and hope we are successfully separating the quantity from the ingredient. This is where repeating groups come in handy. The output of these fields is an object that can be looped through.

Repeating groups in fields.json

// Repeating field group example { "id" : "ingredients", "name" : "ingredients", "label" : "Ingredients", "required" : false, "locked" : false, "occurrence" : { "min" : 1, "max" : null, "sorting_label_field" : "ingredients.ingredient", "default" : null }, "children" : [ { "id" : "ingredients.ingredient", "name" : "ingredient", "label" : "Ingredient", "required" : false, "locked" : false, "validation_regex" : "", "allow_new_line" : false, "show_emoji_picker" : false, "type" : "text", "default" : "Water" }, { "id" : "ingredients.quantity", "name" : "quantity", "label" : "Quantity", "required" : false, "locked" : false, "display" : "text", "min" : 0, "step" : 1, "type" : "number", "default" : 1 }, { "id" : "ingredients.measurement", "name" : "measurement", "label" : "Measurement", "help_text" : "Unit of measurement (cups, tbsp, etc.)", "required" : false, "locked" : false, "allow_new_line" : false, "show_emoji_picker" : false, "type" : "text", "default" : "cups" } ], "type" : "group", "default" : [ { "ingredient" : "Water", "quantity" : 1, "measurement" : "cups" } ] }

Looping through repeating fields in modules

<h2>Ingredients</h2> <ul> {% for ingredient in module.ingredients %} <li> <button data-quantity="{{ ingredient.quantity }}" data-unit="{{ ingredient.measurement }}" data-ingredient="{{ ingredient.ingredient }}"> Add to cart </button> {{ ingredient.quantity }} {{ ingredient.measurement }} {{ ingredient.ingredient }} </li> {% endfor %} </ul>

Repeater options

To make the editing experience better and prevent content editors from providing values that you have not programmatically accommodated for, we allow you to set minimum and maximum values for how many items content creators can add to a repeating field or repeating group. 

For repeating groups you can also set which field acts as the label for that item when viewing the repeater.

Max number of occurences
"occurrence" : { "min" : 1, "max" : 4, "sorting_label_field" : "ingredients.ingredient", }
Repeater Options
ParameterTypeDescription Default

Maximum number of occurrences of this group. Prevents the content creator from adding more than this number of items in the UI.


Minimum number of occurrences of this field group. Prevents users from having less than this number of items in the UI.


This is the field id, of the field to pull text from to show in the UI on the draggable cards. The default for this is the first field in the group.

Inherited fields

The inherited_value property can be configured to make a field inherit its default value from other fields. To set a field's entire default value from another field's value, set the default_value_path to the field name path of the target field. When default_value_path is set, it'll ignore any default set on the field.

To access other fields' values, paths must include module. at the beginning as if you were accessing the value in the module's HubL code.

// Inherited fields { "name": "body_font", "type": "font", "default": { "font": "Helvetica", "color": "#C27BA0" } }, { "name": "h1_font", "type": "font", "default": {}, "inherited_value": { "default_value_path": "module.body_font" } }

For complex fields (fields whose values are objects), users can have more granularity over which properties get inherited through property_value_path. Any paths referred in inherited_value can also include keys from a field's value for complex fields - for example, color fields have object values that contain the color itself as well as opacity. So to get a color's actual color value without the opacity, the path would end in .color. For example, a font field can inherit just its color from a separate color field:

// Inherited fields with objects { "name": "secondary_color", "type": "color", "default": { "color": "#C27BA0", "opacity": 100 } }, { "name": "h1_font", "type": "font", "default": { "font": "Helvetica", "size": 12, "size_unit": "px" }, "inherited_value": { "property_value_paths": { "color": "module.secondary_color.color" } } }

You can also combine the effects of default_value_path and property_value_paths to inherit a default value from one field while inheriting a specific property value from a different field:

// combining the effects of default_value_path and property_value_paths { "name": "body_font", "type": "font", "default": { "font": "Helvetica", "color": "#000000" } }, { "name": "secondary_color", "type": "color", "default": { "color": "#C27BA0", "opacity": 100 } }, { "name": "h1_font", "type": "font", "default": {}, "inherited_value": { "default_value_path": "module.body_font", "property_value_paths": { "color": "module.secondary_color.color" } } }

If a field inherits from another field but then gets directly overridden at the page level or in theme settings, its connection to the controlling field gets severed. Any other fields attached via default_value_path or property_value_paths will no longer affect the value of the field.

Field visibility

When defining custom module and theme fields, you can configure when a field appears by adding the visibility object to the field in the fields.json file. For example, you can set a form module to display a rich text area when the thank you message is selected, but a page selector when a redirect is selected.

You can set visibility based on the value of a controlling_field, or based on a specific property within that field using the property parameter.

You can also apply visibility to an individual field, or to a group of fields to control visibility for all elements in the group.

"visibility" : { "controlling_field" : "field_name", "controlling_value_regex" : "regular_expression_in_controlling_field", "property": "src", "operator" : "EQUAL" }
Use this table to describe parameters / fields

The name of the field that controls the display condition.


The regular expression in the controlling field that needs to be present for the field to display. The regex must match the entire string (not a subset) and is run case-sensitively. 


The operator that defines how the controlling_value_regex value needs to be met. Operators can be one of: 


Sets visibility based on a specific property of the target field. For example, you can enable visibility when an image field's src property is equal to a specific value. By default, if no value is provided for this field, visibility is based on the stringified value of controlling_value_regex.

The visibility attribute can support only one criteria at a time. To include multiple criteria with multiple operators, as well as order of operations, you can use advanced_visibility.

"visibility_rules" : "ADVANCED", "advanced_visibility" : { "boolean_operator" : "AND", "criteria" : [{ "controlling_field" : "field_name", "controlling_value_regex" : "regular_expression_in_controlling_field", "operator" : "MATCHES_REGEX" }, { "controlling_field" : "field_name", "controlling_value_regex" : "regular_expression_in_controlling_field", "operator" : "EQUAL" }] }
Use this table to describe parameters / fields

By default, this value is set to SIMPLE. To use advanced_visibility, set to ADVANCED.


The boolean operator for the conditional criteria. Can be AND or OR.


An array of visibility objects that defines the conditional criteria that needs to be met for the field to display.


The name of the field that controls the display condition.


The value in the controlling field that needs to be met to display the field. When using the MATCHES_REGEX operator, the regex must match the entire string (not a subset) and is run case-sensitively.

A field with a controlling_field_path but no controlling_value_regex is visible if the controlling field has any non-null, non-blank value.


The operator that defines how the controlling_value_regex value needs to be met. Operators can be one of: 


Regex syntax is required when using MATCHES_REGEX.

As an example, below is the first portion of code from the default payments module. To review the full code, you can clone the module in HubSpot, then download into your local environment to view the module's fields.json file.

[ { "id" : "payment", "name" : "payment", "display_width" : null, "label" : "Payment", "required" : true, "locked" : false, "type" : "payment", "default" : { "id" : null, "properties" : { } } }, { "id" : "checkout_location", "name" : "checkout_location", "display_width" : null, "label" : "Checkout behavior", "required" : false, "locked" : false, "visibility" : { "controlling_field" : "payment", "controlling_value_regex" : "id\":\\d+", "operator" : "MATCHES_REGEX" }, "display" : "radio", "choices" : [ [ "new_tab", "Open in a new tab" ], [ "overlay", "Sliding overlay" ] ], "type" : "choice", "default" : "new_tab" }, { "id" : "button_text", "name" : "button_text", "display_width" : null, "label" : "Button text", "required" : true, "locked" : false, "validation_regex" : "", "visibility" : { "controlling_field" : "payment", "controlling_value_regex" : "id\":\\d+", "operator" : "MATCHES_REGEX" }, "allow_new_line" : false, "show_emoji_picker" : false, "type" : "text", "default" : "Checkout" }, { "id" : "icon", "name" : "icon", "display_width" : null, "label" : "Icon", "required" : false, "locked" : false, "visibility_rules" : "ADVANCED", "advanced_visibility" : { "boolean_operator" : "AND", "criteria" : [ { "controlling_field" : "payment", "controlling_value_regex" : "id\":\\d+", "operator" : "MATCHES_REGEX" }, { "controlling_field" : "add_icon", "controlling_value_regex" : "true", "operator" : "EQUAL" } ], "children" : [ ] }, "children" : [ { "id" : "icon.icon", "name" : "icon", "display_width" : null, "label" : "Icon", "required" : true, "locked" : false, "icon_set" : "fontawesome-5.0.10", "type" : "icon", "default" : { "name" : "hubspot", "type" : "SOLID", "unicode" : "f3b2" } }, { "id" : "icon.position", "name" : "position", "display_width" : null, "label" : "Position", "required" : true, "locked" : false, "display" : "select", "choices" : [ [ "left", "Left" ], [ "right", "Right" ] ], "type" : "choice", "default" : "left" } ], "tab" : "CONTENT", "expanded" : false, "type" : "group" }, // rest of fields.json code ]

The above code results in the following behavior:

  • The first field (payment) is a required field (dropdown menu) that lets the content creator select a specific payment link. In HubSpot, a content creator will see the following when first adding the module to the page:


  • Once a payment link is selected, the three fields that follow (checkout_location, button_text, and icon) will appear. This is because the fields have a visibility attribute which is controlled by the payment field and requires an ID value in the payment field's id parameter.

The icon field itself uses advanced_visibility to appear only when there's a payment link present in the payment field AND when the add_icon checkbox is selected.

In addition to setting visibility within fields.json, You can also set visibility in the design manager by editing a field's Display conditions options.

After setting visibility in the design manager, you can fetch the module using the CLI to view the visibility attribute in the module's fields.json file.

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