Blocks
This guide shows how to contribute a new block, add a block to the editor's "Add block" catalogue, or override a core block from inside a Nuxt module. A Nuxt module can be internal (living in apps/web/modules/) or installed as an npm package under node_modules/.
For the underlying mechanism, that is, discovery, load order, and override precedence, see Blocks discovery and overrides.
Before you start
Ensure your module uses the standard runtime folder:
- Internal Nuxt module:
apps/web/modules/<module>/runtime/components/blocks/ - Installed npm package:
<package>/runtime/components/blocks/
The loader in the shop only picks up files below one of these paths.
Add a new block
Create a folder for the block under
runtime/components/blocks/, named exactly like the block'snamefield (for example,MyBlock).Add the renderable component
MyBlock.vueand its paired form componentMyBlockForm.vuenext to it. Both live in the same folder and are picked up together by the loader.Add
icon.svgin the same folder. The file is loaded raw and inlined into the catalogue button.Add
defaults.tsthat exportsgetBlocksList():tsimport type { BlocksList } from '~/composables/useBlocksList/types'; export const getBlocksList = (): BlocksList => ({ 'my-block': { category: 'my-block', title: 'My Block', blockName: 'MyBlock', accessControl: ['content'], variations: [ { title: 'Default', image: 'https://example.com/my-block-preview.png', template: { en: { name: 'MyBlock', type: 'content', meta: { uuid: '00000000-0000-4000-8000-000000000001' }, content: { text: 'Hello world' }, }, de: { name: 'MyBlock', type: 'content', meta: { uuid: '00000000-0000-4000-8000-000000000002' }, content: { text: 'Hallo Welt' }, }, }, }, ], }, });The
namefield on eachtemplatemust match the basename of your.vuefile. This is what the renderer uses to look up the component and what the override system uses to identify a block.Restart the dev server. The block appears in the catalogue for every page context listed in
accessControl.
Optional: createDefault
If the block also needs a blank factory (for example, so that other blocks can insert it as a child), add a createDefault export:
import type { Block } from '@plentymarkets/shop-api';
export const createDefault = (): Block => ({
name: 'MyBlock',
type: 'content',
meta: { uuid: crypto.randomUUID() },
content: { text: '' },
});Override a core block
Overriding a core block usually means replacing both its Vue component and its catalogue entry. Do only what you need, the two are independent.
Replace the Vue component
Create
runtime/components/blocks/<Name>/<Name>.vuein your module using the same basename as the core block file you want to replace. If the block ships a form, replace<Name>Form.vuethe same way, in the same folder.Add the override marker as the first meaningful line in each replacement Vue file:
vue<!-- @overrides-core-block --> <template> <!-- your replacement markup --> </template> <script setup lang="ts"> // … </script>Without the marker,
nuxt buildfails when theFAIL_BUILD_ON_UNMARKED_BLOCK_OVERRIDESenvironment variable is set (CI sets it).
Because the loader processes core first and then Nuxt modules, and each .vue file overwrites the previous entry keyed by basename, your file replaces the core component everywhere.
Replace the catalogue entry
Add
runtime/components/blocks/<Name>/defaults.tsand exportgetBlocksList().Mark the category with
override: trueand use the same block name as the core variation you want to replace:tsimport type { BlocksList } from '~/composables/useBlocksList/types'; export const getBlocksList = (): BlocksList => ({ text: { category: 'text', title: 'Text', blockName: 'TextCard', accessControl: ['content', 'productCategory', 'product'], override: true, variations: [ { title: 'Rich Text (customised)', image: 'https://example.com/my-preview.png', template: { en: { name: 'TextCard', // must match the core block name type: 'content', meta: { uuid: '00000000-0000-4000-8000-000000000003' }, content: { /* your defaults */ }, }, de: { /* … */ }, }, }, ], }, });The override match runs on
template.en.name(withtemplate.de.nameas fallback). Any variation in any category that resolves to the same block name is removed from the catalogue and replaced by yours.To hide a core catalogue entry without offering a replacement, return the same category with
override: trueand an emptyvariationsarray. The Vue component stays available for pages that already use it, but merchants can no longer insert new instances from the catalogue.
See also
- Blocks discovery and overrides — full explanation of the mechanism
- Blocks architecture — overview of the blocks system
- Components — how to add or override generic Vue components (non block)