3. How does a Volto add-on work? – Effective Volto – Add-ons

3. How does a Volto add-on work?#

Volto add-on 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 (for example, 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.

3.1. 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 add-on 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 add-on name in the addons key, the add-on's main default export function is executed, being passed the Volto configuration registry. In that function, the add-on 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 add-ons.

3.1.1. Loading a Volto Add-on optional configuration#

Some add-ons 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 add-on name in the addons key in package.json, 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.

3.1.2. Providing add-on configuration#

The default export of your add-on 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 add-on:

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

In effect, Volto does the equivalent of:

import installMyVoltoAddon from 'my-volto-addon'

// ... in the configuration registry setup step:
const configRegistry = installMyVoltoAddon(defaultRegistry);

So the Volto add-on needs to export a default function that receives the Volto configuration registry, is free to change the registry as it sees fit, then it needs to return that registry.

Volto will chain-execute all the add-on configuration functions to compute the final configuration registry.

Warning

An add-on's default configuration method will always be loaded.

3.1.3. Providing optional add-on configurations#

You can export additional configuration functions from your add-on's main index.js.

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

export { loadOptionalBlocks, overrideSomeDefaultBlock };
export default applyConfig;

3.2. Add-on dependencies#

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

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