Eventonomy

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

  1. Run npm run build in your add-on to generate the build/ directory.
  2. Activate the add-on plugin.
  3. Open the Block Editor on any page - search for "Event Video" and insert it.
  4. View the page on the frontend and confirm the block renders with your markup.
  5. Check the browser console for 0 Interactivity store errors.

Related

  • docs/EXTENDING.md §5 - Interactivity store extension surface.
  • docs/EXTENDING.md §6 - evnm_blocks filter details.
  • Blocks & Templates - the stable store API.
  • Recipes Index