How does a Volto add-on works?#

Volto addon packages are just CommonJS packages. The only requirement is that they point the main key of their package.json to a module that exports, as a default function that acts as a Volto configuration loader.

Similarly to how you develop a Plone backend Python Add-on, you can control all aspects of Volto from a Volto Add-on.

This gives you the ability to move all your project configuration, components, customizations and even theme files to an add-on. This has the advantage to render the project configuration empty, so you could at any point not only reuse the add-on(s) outside the current project, but also have the project as simply boilerplate that could be replaced at any point (eg. a Volto update).

An add-on can be published in an npm registry, just as any other package. However, Volto Add-ons should not be transpiled. They should be released as "source" packages.

See @kitconcept/volto-blocks-grid as an example.

kitconcept/volto-blocks-grid

Loading a Volto Add-on in a project#

You should declare in your project that you are using an add-on. This is done in the project package.json, addons key:

{
  "name": "my-nice-volto-project",
  ...
  "addons": [
    "acme-volto-foo-addon",
    "@plone/some-addon",
    "collective-another-volto-addon"
  ],
  ...
}

```{warning}
Adding the addon package to the `addons` key is obligatory! It allows Volto
to treat that package properly and provide it with BabelJS language
features. In Plone terminology, it is like including a Python egg to the
`zcml` section of zc.buildout.

By including the addon name in the addons key, the addon's default export function is executed, being passed the Volto configuration registry. In that function, the addon can customize the registry. The function needs to return the config (Volto configuration registry) object, so that it's passed further along to the other addons.

Loading a Volto Add-on optional configuration#

Some addons might choose to allow the Volto project to selectively load some of their configuration, so they may offer additional optional configuration functions, which you can load by overloading the addon name in the addons package.json key, like so:

{
  "name": "my-nice-volto-project",
  "addons": [
    "acme-volto-foo-addon:loadOptionalBlocks,overrideSomeDefaultBlock",
    "volto-ga"
  ],
}

Note

If coming from the Plone backend development, you could map the main add-on configuration to the default GenericSetup profile, as it's loaded always. These optional configurations could be mapped to optional GenericSetup profiles that could be applied at any time on demand.

Providing add-on configuration#

The default export of your addon main index.js file should be a function with the signature config => config. That is, it should take the global configuration object and return it, possibly mutated or changed. So your main index.js will look like:

export default function applyConfig(config) {
  config.blocks.blocksConfig.faq_viewer = {
    id: 'faq_viewer',
    title: 'FAQ Viewer',
    edit: FAQBlockEdit,
    view: FAQBlockView,
    icon: chartIcon,
    group: 'common',
    restricted: false,
    mostUsed: true,
    sidebarTab: 1,
    security: {
      addPermission: [],
      view: [],
    },
  };
  return config;
}

And the package.json file of your addon:

{
  "main": "src/index.js",
}

Warning

An addon's default configuration method will always be loaded.

Providing optional add-on configurations#

You can export additional configuration functions from your addon's main index.js.

import applyConfig, {loadOptionalBlocks,overrideSomeDefaultBlock} from './config';

export { loadOptionalBlocks, overrideSomeDefaultBlock };
export default applyConfig;

Addon dependencies#

Addons can depend on any other JavaScript package, but they can also depend on other Volto addons. To do this, specify the name of your Volto addon dependency in your dependencies key of package.json and create a new addons key in the package.json of your addon, where you specify the extra Volto addon dependency.

By doing this, the addons can "chain-load" one another, so you don't have to keep track of intermediary dependencies.