Menus and Navigation

Last updated:

Include navigation menus on your website to help users find the information that the need. Navigation menus are often located in the headers, sidebars, and footers of a website. HubSpot provides a few built-in solutions for adding menus to your pages, depending on your use case, but you can also create your own menus when needed. Your account also includes a settings page for creating and managing menus, which the various menu methods can reference.

  • Default menus: HubSpot provides two default menu types that can be used as needed out of the box. These menus can be added as modules in the page editor in drag and drop areas or in templates, or you can add them to custom modules using HubL tags.
    • Menu: commonly used for global navigation, such as in the website header or footer, the default standard menu enables you to select a menu that you've configured in your navigation settings, then configure it further with options such as maximum levels, display settings, and orientation.
    • Simple menu: commonly used for page-specific navigation, such as pillar pages, the simple menu module enables you to create menus at the page level. Rather than reference a menu that you've built in your navigation settings, simple menu items are managed in the content editor and have fewer configuration options than the standard menu. This enables content creators to update menus on specific pages as needed without impacting global navigation.
  • Custom menus: when the default menu options don't fit your needs, you can create your own custom solutions. This can range from building custom modules that include default menus using the standard menu and simple_menu HubL tags, to using the menu() HubL function to build out a completely custom solution using repeater groups or HubDB. That being said, when building a complicated custom menu, you should keep in mind the editor experience. In many cases, it may make more sense to use the menu and simple_menu fields in tandem with the menu() function so that there's a balance between custom solution and intuitive editing experience.

Below, learn more about the different ways to include menus on pages and how to manage navigation settings in HubSpot.

In each account, HubSpot includes navigation settings so that you can create multi-level menus to reference in menu modules and tags. This creates a single source of truth for a set of menu items, so you'll only need to update a menu once to update all pages referencing that menu. You can create as many menus as needed, and each menu comes with options for cloning, deleting, renaming, and displaying revision history.

To create and manage menus in HubSpot, navigate to SettingsWebsiteNavigation menus. Learn more about navigation settings in HubSpot's Knowledge Base.

Navigation settings area

When building a custom module, the easiest way to get the menu ID is by creating a menu field. This field enables the content creator to select a menu from a dropdown menu, and returns the menu ID. If you need to hardcode the menu ID, you can retrieve it from the URL when viewing the menu in the navigation settings page. 

menu-id-in-urlNote that, when you first arrive at the page, the default menu ID will not display in the URL. To get the ID for that default menu, you'll need to select a different menu, then select the default menu again.

A best practice for site headers, which often contain the lengthy main navigation, is to provide a "skip to content" link. This helps users navigating by keyboard, skipping over lengthy menus.

HubL menu tags

Use the menu and simple_menu HubL tags to add menu functionality to custom modules. Adding the tag to a module will render the menu on the page. To enable content creators to configure the menu's options in the page editor, you'll need to include the menu or simple menu field in the module as well.

Below, learn more about each type of menu tag.

Standard menu

The HubL Menu tag generates standard menu HTML with class names already provided for depth levels, active states, and if the item has children. The menu tag can be used within custom modules making it an easy way to create navigation menus for main nav's and sidebar navigation. This tag expects you to provide the menu ID.

{% menu "menu" %} {% menu "my_menu" id=456, site_map_name='Default', overrideable=False, root_type='site_root', flyouts='true', max_levels='2', flow='horizontal', label='Advanced Menu' %}

Simple menu

The simple menu tag functions just like the menu tag by generating standard menu HTML with class names for depth levels, active states, and if the item has children. The difference is that this tag expects you to provide a dict of the menu structure instead of a menu ID. This is useful for when you want a module's fields to determine the structure of a menu instead of using the navigation settings. For example, you may want to use this type of module for the table of contents of a pillar page.

{% simple_menu menu_tree=[{"contentType": null, "subCategory": null, "pageLinkName": null, "pageLinkId": null, "isPublished": false, "categoryId": null, "linkParams": null, "linkLabel": "Home", "linkTarget": null, "linkUrl": "http://www.hubspot.com", "children": [], "isDeleted": false}, {"contentType": null, "subCategory": null, "pageLinkName": null, "pageLinkId": null, "isPublished": false, "categoryId": null, "linkParams": null, "linkLabel": "About", "linkTarget": null, "linkUrl": "http://www.hubspot.com/internet-marketing-company", "children": [{"contentType": null, "subCategory": null, "pageLinkName": null, "linkUrl": "http://www.hubspot.com/company/management", "isPublished": false, "children": [], "linkParams": null, "linkLabel": "Our Team", "linkTarget": null, "pageLinkId": null, "categoryId": null, "isDeleted": false}], "isDeleted": false}, {"contentType": null, "subCategory": null, "pageLinkName": null, "pageLinkId": null, "isPublished": false, "categoryId": null, "linkParams": null, "linkLabel": "Pricing", "linkTarget": null, "linkUrl": "http://www.hubspot.com/pricing", "children": [], "isDeleted": false}] %}

Default menu modules

HubSpot provides default modules that you can add to coded templates, as well as pages through the page editor when a template includes drag and drop areas. Each module will have a different editing experience, with the standard menu having more configuration options than the simple menu.

Because modules cannot be nested, you cannot place these modules inside of other modules. Instead, you should use the menu or simple menu tags.

{% module "main_nav" path="@hubspot/menu", label="Menu" id="123456" %} {% module "menu" path="@hubspot/simple_menu", label="Simple Menu" menu_tree=[{"contentType": null, "subCategory": null, "pageLinkName": null, "pageLinkId": null, "isPublished": false, "categoryId": null, "linkParams": null, "linkLabel": "Home", "linkTarget": null, "linkUrl": "http://www.hubspot.com", "children": [], "isDeleted": false}, {"contentType": null, "subCategory": null, "pageLinkName": null, "pageLinkId": null, "isPublished": false, "categoryId": null, "linkParams": null, "linkLabel": "About", "linkTarget": null, "linkUrl": "http://www.hubspot.com/internet-marketing-company", "children": [{"contentType": null, "subCategory": null, "pageLinkName": null, "linkUrl": "http://www.hubspot.com/company/management", "isPublished": false, "children": [], "linkParams": null, "linkLabel": "Our Team", "linkTarget": null, "pageLinkId": null, "categoryId": null, "isDeleted": false}], "isDeleted": false}, {"contentType": null, "subCategory": null, "pageLinkName": null, "pageLinkId": null, "isPublished": false, "categoryId": null, "linkParams": null, "linkLabel": "Pricing", "linkTarget": null, "linkUrl": "http://www.hubspot.com/pricing", "children": [], "isDeleted": false}] %}

Standard menu markup

Default menu modules are powered by their respective HubL menu tags (menu and simple_menu) to generate standard menu HTML. Like other HubSpot modules, menu modules are wrapped in module wrapper markup. These div and span tags make the module editable with the content editor. The menu markup of both the menu and simple menu modules is the same, with the exception of some of the classes applied to the wrapper and menu containers.

<div id="hs_cos_wrapper_widget_1711642118872" class="hs_cos_wrapper hs_cos_wrapper_widget hs_cos_wrapper_type_module widget-type-menu" style="" data-hs-cos-general-type="widget" data-hs-cos-type="module"> <span id="hs_cos_wrapper_widget_1711642118872_" class="hs_cos_wrapper hs_cos_wrapper_widget hs_cos_wrapper_type_menu" style="" data-hs-cos-general-type="widget" data-hs-cos-type="menu"> <div id="hs_menu_wrapper_widget_1711642118872_" class="hs-menu-wrapper active-branch flyouts hs-menu-flow-horizontal" role="navigation" data-sitemap-name="default" data-menu-id="162449947934" aria-label="Navigation Menu"> <ul role="menu"> <li class="hs-menu-item hs-menu-depth-1 hs-item-has-children" role="none"> <a href="javascript:;" aria-haspopup="true" aria-expanded="false" role="menuitem">Menu item 1</a> <ul role="menu" class="hs-menu-children-wrapper"> <li class="hs-menu-item hs-menu-depth-2" role="none"><a href="//freecrmtest.hubspotpagebuilder.com/test" role="menuitem">A</a></li> <li class="hs-menu-item hs-menu-depth-2" role="none"><a href="https://www.wikipedia.org/" role="menuitem">B</a></li> </ul> </li> <li class="hs-menu-item hs-menu-depth-1 hs-item-has-children" role="none"> <a href="javascript:;" aria-haspopup="true" aria-expanded="false" role="menuitem">Menu item 2</a> <ul role="menu" class="hs-menu-children-wrapper"> <li class="hs-menu-item hs-menu-depth-2" role="none"><a href="//freecrmtest.hubspotpagebuilder.com/test" role="menuitem">A</a></li> <li class="hs-menu-item hs-menu-depth-2" role="none"><a href="https://www.hubspot.com/new-page" role="menuitem">B</a></li> <li class="hs-menu-item hs-menu-depth-2" role="none"><a href="https://www.hubspot.com/blah" role="menuitem">C</a></li> </ul> </li> </ul> </div> </span> </div>

As shown above, the actual menu renders as a ul wrapped in a div with the hs-menu-wrapper class. This wrapper will have additional classes based on how the module is configured in the page editor, such as enabling flyouts. Learn more about classes added by these settings below.

Within the ul, each menu item is an a tag wrapped in a li. The li tag has a class that indicates the depth of the item in the menu tree (e.g., hs-menu-depth-1). When a menu item contains a nested child item, the corresponding li will have the additional class of hs-item-has-children. The child menu renders as a nested ul with the class hs-menu-children-wrapper.

When you visit a page that is included in your menu tree, the class active-branch is added to the parent li items and a class of active is added to that page's particular li item.

Standard menu styling

At the module level, whether editing a menu module in the page editor or editing a menu field in a custom module, you'll have a few configuration options. The MenuAdvanced menu type, and Max levels fields enable you to control the menu items are rendered as li in the page markup. But the orientation and flyouts options will impact the CSS selectors added to the menu wrapper div. You can then target these selectors in your CSS.

menu-options-in-editor

Below, learn more about the classes that are added to the menu wrapper div depending on these field settings.

Use this table to describe parameters / fields
ClassDescription
hs-menu-flow-horizontal

Added to the wrapper div when the menu is set to horizontal orientation.

hs-menu-flow-vertical

Added to the wrapper div when the menu is set to vertical orientation.

flyouts

Added to the wrapper div when Enable flyouts is selected.

no-flyouts

Added to the wrapper div when Enable flyouts is not selected.

To get you started styling menus, below are some example CSS selectors that can be used to style the menu tag and default menu module.

/* Menus */ .hs-menu-wrapper ul { /* Targets all unordered lists within HubSpot menus */ } /* Horizontal Menu ========================================================================== */ .hs-menu-wrapper.hs-menu-flow-horizontal ul { /* Targets all unordered lists within horizontal menus */ } .hs-menu-wrapper.hs-menu-flow-horizontal ul li{ /* Targets all list items within horizontal menus */ } .hs-menu-wrapper.hs-menu-flow-horizontal ul li a{ /* Targets all links within horizontal menus */ } .hs-menu-wrapper.hs-menu-flow-horizontal > ul { /* Targets the top-level unordered list within horizontal menus */ } .hs-menu-wrapper.hs-menu-flow-horizontal > ul li.hs-menu-depth-1 { /* Targets top-level list items within the top-level unordered list */ } .hs-menu-wrapper.hs-menu-flow-horizontal > ul li a { /* Targets top-level list links within the top-level unordered list */ } .hs-menu-wrapper.hs-menu-flow-horizontal > ul li.hs-item-has-children { /* Targets list items with children with the top-level unordered list */ } .hs-menu-wrapper.hs-menu-flow-horizontal.flyouts > ul li.hs-item-has-children ul.hs-menu-children-wrapper { /* Targets second-level unordered lists when flyouts are enabled (for styling dropdowns) */ } .hs-menu-wrapper.hs-menu-flow-horizontal > ul li.hs-item-has-children ul.hs-menu-children-wrapper li a { /* Targets links within second-level unordered lists */ } .hs-menu-wrapper.hs-menu-flow-horizontal.flyouts > ul li.hs-item-has-children ul.hs-menu-children-wrapper li.hs-item-has-children ul.hs-menu-children-wrapper { /* Targets third-level unordered lists (for styling dropdowns)*/ } .hs-menu-wrapper.hs-menu-flow-horizontal.flyouts > ul li.hs-item-has-children:hover > ul.hs-menu-children-wrapper { /* Targets second-level unordered list when top-level menu item is hovered (use to reveal dropdowns) */ } .hs-menu-wrapper.hs-menu-flow-horizontal > ul li.hs-item-has-children.active-branch{ /* Targets top-level active branch unordered list */ } .hs-menu-wrapper.hs-menu-flow-horizontal > ul li.hs-item-has-children.active-branch > ul.hs-menu-children-wrapper { /* Targets second-level unordered list within active branch */ } .hs-menu-wrapper.hs-menu-flow-horizontal li.active a{ /* Targets the link within the active list item */ } /* Vertical Menu ========================================================================== */ .hs-menu-wrapper.hs-menu-flow-vertical ul { /* Targets all unordered lists within vertical menus */ } .hs-menu-wrapper.hs-menu-flow-vertical ul li a { /* Targets all list items within vertical menus */ } .hs-menu-wrapper.hs-menu-flow-vertical ul li a { /* Targets all links within vertical menus */ } .hs-menu-wrapper.hs-menu-flow-vertical > ul { /* Targets the top-level unordered list within vertical menus */ } .hs-menu-wrapper.hs-menu-flow-vertical > ul li.hs-menu-depth-1 > a { /* Targets top-level links in vertical menus */ } .hs-menu-wrapper.hs-menu-flow-vertical > ul li.hs-item-has-children { /* Targets top-level list items with children */ } /* No flyouts ========================================================================== */ .hs-menu-wrapper.hs-menu-flow-vertical.no-flyouts .hs-menu-children-wrapper { /* Targets child menus when flyouts are disabled */ } .hs-menu-wrapper.hs-menu-flow-horizontal.no-flyouts > ul li.hs-item-has-children ul.hs-menu-children-wrapper { /* Targets second-level child menus when flyouts are disabled */ }

HubL menu() function

The menu() function exists to enable you to create fully custom menu structures. It returns an object that you can iterate through to generate a menu, there are many provided properties for the menu items. Be aware when you use the menu function you are fully responsible for the accessibility of your menu, the structure, and the styling.

{% set node = menu(987) %} {% for child in node.children %} {{ child.label }}<br> {% endfor %} {% set default_node = menu("default") %} {% for child in default_node.children %} {{ child.label }}<br> {% endfor %}

The HubSpot CMS Theme Boilerplate contains an example menu module built with the menu() function. You can modify that to meet your needs to make complicated menus.


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