Recipe: Add a Custom Block
Goal: Register a Gutenberg block that inherits the shared eventonomy Interactivity store and reads the canonical REST envelope - without duplicating store or API logic.
Seams Used
| Seam | Type | Purpose |
|---|---|---|
evnm_blocks |
Filter | Register the block with Eventonomy's BlockRegistrar. |
evnm_get_view_data() |
Function | Read server-side view data (event, config) for block render.php. |
store('eventonomy', …) |
JS | Merge additional state or actions into the shared store. |
Step 1 - Register the Block
In your plugin's PHP bootstrap (or in a Provider - see Recipe: Extend with a Provider):
add_filter( 'evnm_blocks', function ( array $blocks ): array {
$blocks['my-addon/event-video'] = [
'path' => __DIR__ . '/build/event-video', // directory with block.json
'uses_store' => true, // inherit the shared store
'view_data' => [ 'event' ], // request 'event' hydration
];
return $blocks;
} );
BlockRegistrar picks this up automatically. The path must contain a valid block.json.
Step 2 - Build the Block Files
Minimal directory layout:
build/event-video/
├── block.json
├── index.js (editor script)
├── view.js (frontend, imported by the block)
└── style.css
block.json (minimum required fields):
{
"name": "my-addon/event-video",
"title": "Event Video",
"category": "eventonomy",
"editorScript": "file:./index.js",
"viewScriptModule": "file:./view.js",
"style": "file:./style.css"
}
Step 3 - Read View Data in render.php
<?php
// build/event-video/render.php
defined( 'ABSPATH' ) || exit;
// evnm_get_view_data() returns the hydrated array your block declared in 'view_data'.
$view_data = function_exists( 'evnm_get_view_data' )
? evnm_get_view_data( 'event', get_the_ID() )
: [];
$vimeo_url = evnm_get_meta( 'event', (int) ( $view_data['id'] ?? 0 ), 'vimeo_url', '' );
?>
<div
<?php echo wp_kses_data( get_block_wrapper_attributes() ); ?>
data-wp-interactive="eventonomy"
data-wp-context="<?php echo esc_attr( wp_json_encode( [ 'vimeoUrl' => $vimeo_url ] ) ); ?>"
>
<div class="my-addon-video" data-wp-bind--hidden="!context.vimeoUrl">
<iframe
data-wp-bind--src="context.vimeoUrl"
allow="autoplay; fullscreen"
loading="lazy"
></iframe>
</div>
</div>
Step 4 - Merge into the Shared Store
// build/event-video/view.js
import { store, getContext } from '@wordpress/interactivity';
store( 'eventonomy', {
state: {
// Derived state: a getter that reads from this block's context.
get hasVideo() {
return !! getContext().vimeoUrl;
},
},
actions: {
*loadVideo() {
const ctx = getContext();
ctx.loading = true;
// ... fetch from a custom REST endpoint if needed.
ctx.loading = false;
},
},
} );
Calling store('eventonomy', …) merges your additions - it does not replace the existing store.
Step 5 - Add Meta to the Event Editor (Optional)
If the block needs a custom field persisted on the event, add it to the event editor form and persist it via hooks:
// Add a field to the event editor form.
add_filter( 'evnm_event_form_fields', function ( array $fields ): array {
$fields[] = [
'id' => 'vimeo_url',
'label' => __( 'Vimeo URL', 'my-addon' ),
'type' => 'url',
];
return $fields;
} );
// Persist on create.
add_action( 'evnm_after_create_event', function ( array $event, array $context ): void {
if ( ! empty( $event['meta']['vimeo_url'] ) ) {
evnm_update_meta( 'event', $event['id'], 'vimeo_url', esc_url_raw( $event['meta']['vimeo_url'] ) );
}
}, 10, 2 );
// Persist on update (same pattern).
add_action( 'evnm_after_update_event', function ( array $event, array $changed_keys, array $context ): void {
if ( isset( $event['meta']['vimeo_url'] ) ) {
evnm_update_meta( 'event', $event['id'], 'vimeo_url', esc_url_raw( $event['meta']['vimeo_url'] ) );
}
}, 10, 3 );
Verify It Worked
- Run
npm run buildin your add-on to generate thebuild/directory. - Activate the add-on plugin.
- Open the Block Editor on any page - search for "Event Video" and insert it.
- View the page on the frontend and confirm the block renders with your markup.
- Check the browser console for
0Interactivity store errors.
Related
docs/EXTENDING.md§5 - Interactivity store extension surface.docs/EXTENDING.md§6 -evnm_blocksfilter details.- Blocks & Templates - the stable store API.
- Recipes Index