Volto-dropdownmenu Customization path registeration

What is correct way of registering customizationPaths of volto-dropdownmenu ? Here is my sample package.json

  "name": "frontend",
  "description": "A Volto-powered Plone frontend",
  "license": "MIT",
  "version": "1.0.0",
  "scripts": {
    "start": "razzle start",
    "preinstall": "if [ -f $(pwd)/mrs.developer.json ]; then if [ -f $(pwd)/node_modules/.bin/missdev ]; then yarn develop; else yarn develop:npx; fi; fi",
    "postinstall": "yarn omelette && yarn patches",
    "omelette": "ln -sf node_modules/@plone/volto/ omelette",
    "patches": "/bin/bash patches/patchit.sh > /dev/null 2>&1 ||true",
    "build": "razzle build",
    "lint": "./node_modules/eslint/bin/eslint.js 'src/**/*.{js,jsx}'",
    "lint:fix": "./node_modules/eslint/bin/eslint.js --fix 'src/**/*.{js,jsx}'",
    "lint:ci": "./node_modules/eslint/bin/eslint.js -f checkstyle 'src/**/*.{js,jsx}' > eslint.xml",
    "prettier": "./node_modules/.bin/prettier --single-quote --check 'src/**/*.{js,jsx,ts,tsx,json,css,scss,md}'",
    "prettier:fix": "./node_modules/.bin/prettier --single-quote --write 'src/**/*.{js,jsx,ts,tsx,json,css,scss,md}'",
    "prettier:ci": "./node_modules/.bin/prettier --single-quote --check 'src/**/*.{js,jsx,ts,tsx,json,css,scss,md}'",
    "stylelint": "stylelint 'theme/**/*.{css,less}' 'src/**/*.{css,less}'",
    "stylelint:overrides": "stylelint 'theme/**/*.overrides' 'src/**/*.overrides'",
    "stylelint:fix": "yarn stylelint --fix && yarn stylelint:overrides --fix",
    "test": "razzle test --env=jest-environment-jsdom-sixteen --passWithNoTests",
    "cypress:run": "NODE_ENV=test cypress run",
    "cypress:open": "NODE_ENV=test cypress open",
    "cypress:start-frontend": "RAZZLE_API_PATH=http://localhost:55001/plone yarn start",
		"cypress:test-acceptance-server": "make test-acceptance-server",
		"cy:test:fixture:setup": "node cypress/support/reset-fixture.js",
		"cy:test:fixture:teardown": "node cypress/support/reset-fixture.js teardown",
    "ci:start-backend": "make start-test-backend",
    "ci:start-frontend": "RAZZLE_API_PATH=http://localhost:55001/plone yarn build && start-test start:prod http-get://localhost:3000 cypress:run",
    "ci:cypress:run": "start-test ci:start-backend http-get://localhost:55001/plone ci:start-frontend",
    "start:prod": "NODE_ENV=production node build/server.js",
    "i18n": "rm -rf build/messages && NODE_ENV=production i18n",
    "develop:npx": "npx -p mrs-developer missdev --config=jsconfig.json --output=addons --fetch-https",
    "develop": "missdev --config=jsconfig.json --output=addons --fetch-https",
    "storybook": "start-storybook -p 6006",
    "build-storybook": "build-storybook"
  "private": false,
  "workspaces": [],
  "addons": [],
  "jest": {
    "modulePathIgnorePatterns": [
    "transform": {
      "^.+\\.js(x)?$": "babel-jest",
      "^.+\\.css$": "jest-css-modules",
      "^.+\\.scss$": "jest-css-modules",
      "^.+\\.(png)$": "jest-file",
      "^.+\\.(jpg)$": "jest-file",
      "^.+\\.(svg)$": "./node_modules/@plone/volto/jest-svgsystem-transform.js"
    "transformIgnorePatterns": [
    "moduleNameMapper": {
      "@plone/volto/babel": "<rootDir>/node_modules/@plone/volto/babel",
      "@plone/volto/(.*)$": "<rootDir>/node_modules/@plone/volto/src/$1",
      "load-volto-addons": "<rootDir>/node_modules/@plone/volto/jest-addons-loader.js",
      "@package/(.*)$": "<rootDir>/src/$1",
      "~/(.*)$": "<rootDir>/src/$1"
    "coverageThreshold": {
      "global": {
        "branches": 10,
        "functions": 10,
        "lines": 10,
        "statements": 10
    "setupFiles": [
    "globals": {
      "__DEV__": true
  "customizationPaths": [
  "prettier": {
    "trailingComma": "all",
    "singleQuote": true,
    "overrides": [
        "files": "*.overrides",
        "options": {
          "parser": "less"
  "stylelint": {
    "extends": [
    "plugins": [
    "overrides": [
        "files": [
        "customSyntax": "postcss-less"
        "files": [
        "customSyntax": "postcss-less"
    "rules": {
      "prettier/prettier": true,
      "rule-empty-line-before": [
          "except": [
          "ignore": [
    "ignoreFiles": "theme/themes/default/**/*.overrides"
  "browserslist": [
    "last 4 versions",
    "Firefox ESR",
    "not ie 11",
    "not dead"
  "engines": {
    "node": "^12 || ^14 || ^16"
  "dependencies": {
    "@plone/volto": "14.2.0",
    "volto-dropdownmenu": "2.3.0",
    "react-json-print": "0.1.3"
    "@visx/network": "2.1.2"
  "devDependencies": {
    "eslint-plugin-prettier": "3.1.3",
    "jest-junit": "8.0.0",
    "mrs-developer": "*",
    "postcss": "8.3.11",
    "prettier": "2.0.5",
    "@storybook/addon-actions": "^6.3.0",
    "@storybook/addon-controls": "6.3.0",
    "@storybook/addon-essentials": "^6.3.0",
    "@storybook/addon-links": "^6.3.0",
    "@storybook/react": "^6.3.0",
    "stylelint": "14.0.1",
    "stylelint-config-idiomatic-order": "8.1.0",
    "stylelint-config-prettier": "8.0.1",
    "stylelint-prettier": "1.1.2"

So, for example, to customize components/DropdownMenu.jsx you need to place it in src/customizations/volto-dropdownmenu/components/DropdownMenu.jsx.

Let us know if that doesn't work for you.

1 Like

Thank you @tiberiuichim for prompt response.

Not quite sure why the dropdown menu setting editor is not working as shown in the Youtube demo. I tried fresh install multiple times.

I forgot to mention, the "trick" to understanding the customization mechanism is to understand what exactly it means.

Somewhere in code there may be an import. The concept of JS modules and module paths is actually implemented at the bundler level (webpack). So the "shadowing" is something that's being done as a webpack resolve alias. All addon names are also aliased. When Volto's "webpack setup" integration happens at the beginning (this is the job of razzle.config.js), it crawls the src/customizations folders and will register resolve aliases based on the found files there. So, to customize a module from another addon, you have to match, as a path, the addon name (so, if the addon uses a namespace, you have to create a folder with that namespace as name, for example @eeacms/volto-columns-block). Then, considering that you're not trying to replicate the internal paths from the addon, but the "module import path", you omit the src folder that typically exist in addons.

And finally, as a piece of trivia and because it's something we've had to implement, Volto has something that's quite unique, as far as I can tell. To enable seamless customizations and allow relative imports to be also "shadowable", we have a custom webpack resolver plugin that automatically translates all relative imports into "full name paths".


It looks like the custom widget for the menu configuration is not registered. Make sure to enable the addon in your package.json, in the "addons" key. See Introduction - Volto Developer Documentation

The addons keys is the one that tells Volto where to look for "Volto addons loaders". An "addon loader" is the equivalent of a configure.zcml, it's code to wire up internal configuration.


That worked perfectly! Thank you @tiberiuichim

Plone Foundation Code of Conduct