> For the complete documentation index, see [llms.txt](https://docs.colormass.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.colormass.com/dev/configurator/web-components/examples/custom-menu.md).

# Custom Menu

### Goal

A common use case is the integration of the configurator into a shop system or an existing website. As the design options of the built-in menu are limited, it is often necessary to create a separate menu to control the parameters of the configurator.

#### Examples

The following image shows the customised menu from the sample project. The options are generated dynamically based on the available config parameters and control the rendering of the configurator.

<figure><img src="/files/oNeIfyx94R6ST9FQFeLb" alt=""><figcaption><p>Sample configurator with a custom menu UI.</p></figcaption></figure>

### Setting the scene

For this example, we set up a test scene on the colormass platform. It consists of a chair and a configuration node to select from a list of materials for the front and back of the chair.

<figure><img src="/files/It5aUgA51hOT1ApyHQxn" alt=""><figcaption><p>Config group and variants for the material applied to the seat.</p></figcaption></figure>

### Adding the markup

Once the scene has been prepared, the configurator component can be added to the page.

```html
<cm-configurator-main ui-style="default" template-uuid="fb825772-66cd-4001-ae46-d537f194d6a2" use-external-menu="true"></cm-configurator-main>
```

We also add an element that functions as a container for the menu options.

```html
<div class="menu-controls" id="menu-container">
    <!-- Container holds the dynamically rendered menu elements -->
</div>
```

### Adding the scripts

We use the `getParameterList` API to get all available configuration parameters for the current template. For each parameter, we cycle through the available configuration `values` and create a button element. The button uses `setParameter` to update the rendering.

Note: we use a small helper function `htmlToNode` to create the elements to prevent repetition.

```javascript
async function initializeMenu() {
    // clear the menu container
    menuContainer.innerHTML = ""
    // load all available parameters
    parameterList = cmConfigurator.getParameterList()
    for (const parameter of parameterList) {
        // create a container element for each parameter
        const parameterContainer = htmlToNode(`
            <div class="menu-section">
                <h3>${parameter.name}</h3>
                <div class="parameter-flex" id="menu-parameter-values-container"></div>
            </div>
        `)

        for (const parameterValue of parameter.values) {
            // create a button for each possible parameter value
            const parameterValueButton = htmlToNode(`
                <button class="${parameterValue.id === parameter.value ? "active" : ""}">
                    ${parameterValue.name}
                </button>
            `)
            parameterValueButton.onclick = async (event) => {
                updateParameterMenuActiveStates(event.currentTarget)
                await cmConfigurator.setParameter(parameter.id, parameter.parameterType, parameterValue.id)
            }
            parameterContainer.querySelector("#menu-parameter-values-container").appendChild(parameterValueButton)
        }

        menuContainer.appendChild(parameterContainer)
    }
}
```

As soon as the configurator element has finished loading, we add two event listeners to call the above defined render function `initializeMenu`. The `loadingCompleted` event is triggered when the first loading of the configurator is complete. The `changeCompleted` event is triggered when the render parameters have changed.

```javascript
let cmConfigurator, menuContainer, parameterList

customElements.whenDefined("cm-configurator-main").then(() => {
    cmConfigurator = document.querySelector("cm-configurator-main")
    // Initialize UI elements
    menuContainer = document.getElementById("menu-container")
    // Once the configurator has loaded, the menu can be rendered based on the available parameters
    cmConfigurator.addEventListener("loadingCompleted", initializeMenu)
    // Update the menu when available parameter values changed.
    cmConfigurator.addEventListener("configurationLoaded", initializeMenu)
    // Update the menu when available parameter values changed through the setParameter API
    cmConfigurator.addEventListener("changeCompleted", initializeMenu)
})
```

### Putting it all together

The complete example code can also be found in our [sample repository](https://github.com/colormass/samples/tree/prod/configurator-webcomponents/custom-menu/index.html).

```html
<!DOCTYPE html>
<html style="height: 100%">
    <head>
        <link rel="preconnect" href="https://configurator.colormass.com" />
        <!-- Load the colormass configurator styles -->
        <link href="https://configurator.colormass.com/styles.css" rel="stylesheet" />
        <!-- Load the colormass configurator web components -->
        <script src="https://configurator.colormass.com/cm-configurator.js" type="module"></script>

        <link rel="stylesheet" href="../styles/sample.css" />
        <link rel="stylesheet" href="../styles/ui.css" />

        <title>Custom Menu | Example</title>
        <meta charset="UTF-8" />
 
        <script>
            let cmConfigurator, menuContainer, parameterList

            customElements.whenDefined("cm-configurator-main").then(() => {
                cmConfigurator = document.querySelector("cm-configurator-main")
                // Initialize UI elements
                menuContainer = document.getElementById("menu-container")
                // Once the configurator has loaded, the menu can be rendered based on the available parameters
                cmConfigurator.addEventListener("loadingCompleted", initializeMenu)
                // Update the menu when available parameter values changed.
                cmConfigurator.addEventListener("configurationLoaded", initializeMenu)
                // Update the menu when available parameter values changed.
                cmConfigurator.addEventListener("changeCompleted", initializeMenu)
            })

            async function initializeMenu() {
                // clear the menu container
                menuContainer.innerHTML = ""
                // load all available parameters
                parameterList = cmConfigurator.getParameterList()
                for (const parameter of parameterList) {
                    // create a container element for each parameter
                    const parameterContainer = htmlToNode(`
                        <div class="menu-section">
                            <h3>${parameter.name}</h3>
                            <div class="parameter-flex" id="menu-parameter-values-container"></div>
                        </div>
                    `)

                    for (const parameterValue of parameter.values) {
                        // create a button for each possible parameter value
                        const parameterValueButton = htmlToNode(`
                            <button class="${parameterValue.id === parameter.value ? "active" : ""}">
                                ${parameterValue.name}
                            </button>
                        `)
                        parameterValueButton.onclick = async (event) => {
                            updateParameterMenuActiveStates(event.currentTarget)
                            await cmConfigurator.setParameter(parameter.id, parameter.parameterType, parameterValue.id)
                        }
                        parameterContainer.querySelector("#menu-parameter-values-container").appendChild(parameterValueButton)
                    }

                    menuContainer.appendChild(parameterContainer)
                }
            }

            function updateParameterMenuActiveStates(element) {
                // remove active class from all siblings
                const siblings = element.parentElement.children
                for (const sibling of siblings) {
                    sibling.classList.remove("active")
                }
                // set active class for clicked element
                element.classList.add("active")
            }

            function htmlToNode(html) {
                const template = document.createElement("template")
                template.innerHTML = html.trim()
                return template.content.firstChild
            }
        </script>
    </head>
    <body>
        <div class="container">
            <div class="menu">
                <div class="menu-controls" id="menu-container">
                    <!-- Container holds the dynamically rendered menu elements -->
                </div>
            </div>
            <div class="configurator-container">
                <cm-configurator-main ui-style="default" template-uuid="fb825772-66cd-4001-ae46-d537f194d6a2" use-external-menu="true"></cm-configurator-main>
            </div>
        </div>
    </body>
</html>

```
