I've been writing various custom blocks, but have run into a small issue: I'd like to be able to directly reference a custom child block inside a custom parent block. The problem is I already have an InnerBlocks component in the parent.
The child block works fine in a standalone context — its purpose being to fetch data from a third party API on certain events. The index.js is pretty standard:
import edit from './edit';
import save from './save';
import metadata from './block.json';
registerBlockType( metadata.name, {
/**
* @see ./edit.js
*/
edit,
/**
* @see ./save.js
*/
save,
} );
The parent block is currently just a thin wrapper around the core/embed
block with some extra content appended within its save method. When the core/embed
updates, however, an event is triggered that I'd like to pass on to the custom child block.
The parent block currently makes use of the InnerBlocks
component
return (
<div { ...useBlockProps({ className: customClass }) }>
<InnerBlocks
allowedBlocks={['core/embed']}
template={EMBED_TEMPLATE}
templateLock="all"
__experimentalLayout={defaultLayout}
></InnerBlocks>
/* And then ideally... */
<ChildBlock propsAndListeners/>
</div>
)
To be clear: the parent/child blocks are not for public use, they're solely for use at a specific company, complete with their own branding etc, so reuse is not really a priority at this time. The aim is to hand over a gutenberg site builder with custom blocks specific to their needs.
I assumed that I could export the child block from its index.js, but haven't had any luck with this. How do I go about exporting this so I can simply import ChildBlock from '../path
in another file, in much the same way I can reference core blocks?
I've been writing various custom blocks, but have run into a small issue: I'd like to be able to directly reference a custom child block inside a custom parent block. The problem is I already have an InnerBlocks component in the parent.
The child block works fine in a standalone context — its purpose being to fetch data from a third party API on certain events. The index.js is pretty standard:
import edit from './edit';
import save from './save';
import metadata from './block.json';
registerBlockType( metadata.name, {
/**
* @see ./edit.js
*/
edit,
/**
* @see ./save.js
*/
save,
} );
The parent block is currently just a thin wrapper around the core/embed
block with some extra content appended within its save method. When the core/embed
updates, however, an event is triggered that I'd like to pass on to the custom child block.
The parent block currently makes use of the InnerBlocks
component
return (
<div { ...useBlockProps({ className: customClass }) }>
<InnerBlocks
allowedBlocks={['core/embed']}
template={EMBED_TEMPLATE}
templateLock="all"
__experimentalLayout={defaultLayout}
></InnerBlocks>
/* And then ideally... */
<ChildBlock propsAndListeners/>
</div>
)
To be clear: the parent/child blocks are not for public use, they're solely for use at a specific company, complete with their own branding etc, so reuse is not really a priority at this time. The aim is to hand over a gutenberg site builder with custom blocks specific to their needs.
I assumed that I could export the child block from its index.js, but haven't had any luck with this. How do I go about exporting this so I can simply import ChildBlock from '../path
in another file, in much the same way I can reference core blocks?
- what do your blocks do and why do you need to do this? What you're describing doesn't fully make sense, but you're describing things in a super generic way that makes it very difficult to answer the question. On top of that, what you've described sounds heavily like a misunderstanding of how blocks work or how they're meant to be used that probably has a trivial solution that's unrelated to what you've asked about, or maybe not, it's very hard to tell. Use the edit link to add context and examples so that your question is easier to understand. – Tom J Nowell ♦ Commented Feb 18, 2022 at 18:15
- I've added some extra info, I hope it shines a little extra light on things. – verism Commented Feb 18, 2022 at 18:46
- hmm you actually just want to reuse the child block as a user interface component, I've seen this in the past where somebody has tried to use innerblocks to insert a child that provides a pre-chosen image. I'll tell you what I told them, that this is not how blocks work, blocks are not react components or UI components to be reused inside other blocks. If these were HTML tags then it wouldn't make sense logically, blocks are the same. Instead you should use the filters in JS that the editor provides. There's a filter that you can use to add extra attributes, and a filter for the edit component – Tom J Nowell ♦ Commented Feb 18, 2022 at 19:02
- this way you can modify the core embed block to include the additional attributes and user interface react components you want without introducing new blocks. The approach you've attempted to take here is a dead end, and not how you are meant to build blocks, be that trying to use a block as a react component in another block, or trying to use a block as a container for an embed block with a locked template so that you can manipulate its user interface – Tom J Nowell ♦ Commented Feb 18, 2022 at 19:03
- OK, I think that makes sense regarding the embed block - I'll look into filters. So would you recommend putting all the custom functionality into a single block that extends/filters the embed block? – verism Commented Feb 18, 2022 at 19:48
1 Answer
Reset to default 1You cannot embed blocks as react components this way, and you should not try to use other blocks as user interface components by setting innerblocks with a locked template.
Instead, use 2 filters to modify the core/embed block:
- Use
editor.BlockListBlock
oncore/embed
to modify the edit component, wrapping it in your own component that has extra user interface react components - Use the
blocks.registerBlockType
filter to modify thecore/embed
registration to include your additional attributes - Use PHP to adjust how the
core/embed
block is rendered using filters
This way you can use the standard core embed block and no longer need to register new blocks.
For example this adds a button to the toolbar of every block:
import { BlockControls } from '@wordpress/block-editor';
import {
ToolbarButton,
ToolbarGroup,
} from '@wordpress/components';
import { createHigherOrderComponent } from '@wordpress/compose';
import { addFilter } from '@wordpress/hooks';
addFilter(
'editor.BlockListBlock',
'verism/toolbar-button',
AddToolbarButton,
10
);
const AddToolbarButton = createHigherOrderComponent( BlockEdit => {
return ( props ) => {
if ( props.name != 'core/embed' ) {
return <BlockEdit { ...props } />;
}
return (
<Fragment>
<BlockControls>
<ToolbarGroup>
<ToolbarButton
className="verisms-really-nice-button"
label="Verisms really nice button"
onClick={ () => {
console.log( 'button pressed' );
} }
/>
</ToolbarGroup>
</BlockControls>
<BlockEdit { ...props } />
</Fragment>
);
};
}, 'AddToolbarButton' );
https://developer.wordpress.org/block-editor/reference-guides/filters/block-filters/#editor-blockedit
You could insert sidebar panels, textfields, toolbar buttons etc.
You could also filter the attributes in PHP via the block_type_metadata
filter:
https://developer.wordpress.org/block-editor/reference-guides/filters/block-filters/#block_type_metadata