Eventonomy

Blocks & Templates

Eventonomy blocks use the WordPress Interactivity API. They share a single eventonomy store and a canonical REST envelope. Templates are overridable from your theme.

What You Will Learn

  • The shared Interactivity store and its stable surface
  • How to register a custom block that inherits store hydration
  • How to override templates from a child theme
  • The shortcode surface for classic-editor parity
  • The evnm_blocks filter

The Shared Interactivity Store

All blocks share one store namespaced eventonomy. Calling store('eventonomy', …) from a second script merges into it; it does not replace it.

Stable public surface (src/store/index.js):

Member Type Description
state.config object Bootstrap config seeded server-side by Interactivity\Store::seed() via wp_interactivity_state(). Holds restRoot, nonce, the currency block (currency, currencySymbol, currencyPosition, thousandSeparator, decimalSeparator, numberOfDecimals, currencyDecimals), defaultEventDurationMinutes, loggedIn, cardShowImage, gateway, and the server-translated i18n string map.
state.isBusy bool True while an async action is in flight
actions.rsvp() generator action Submit an RSVP for the event in the current block context. Routes to POST /orders instead when the visitor added a donation.
actions.refresh() generator action Re-fetch the context event from GET /events/{id} into context.event
actions.restoreRegistered() action Guest-only: restore the "you are registered" confirmation from the per-browser marker on load

There is no state.currentEvent, no actions.buyTicket() and no callbacks member. Event data lives in per-block context (context.event, context.eventId), not in shared state, so two event blocks on one page never fight over a single "current" event. The store's actions take no arguments - they read getContext().

Extending the store:

import { store, getContext } from '@wordpress/interactivity';

const { state } = store( 'eventonomy', {
    state: {
        get myCustomValue() { return getContext().event?.my_field ?? ''; }
    },
    actions: {
        *loadMyData() {
            const ctx = getContext();
            ctx.busy = true;
            try {
                const cfg = state.config || {};
                const res = yield fetch( `${ cfg.restRoot }/../my-addon/v1/events/${ ctx.eventId }/my-data`, {
                    headers: { 'X-WP-Nonce': cfg.nonce },
                } );
                const result = yield res.json();
                ctx.myData = result.items;
            } finally { ctx.busy = false; }
        },
    },
} );

Use fetch() with the nonce from state.config, not apiFetch. Block front ends run as script modules (viewScriptModule in block.json), and @wordpress/api-fetch is not registered as a script module - importing it from a view module leaves an unresolvable specifier in the import map and the whole module fails to load. Every block Eventonomy ships calls fetch() with state.config.restRoot and an X-WP-Nonce: state.config.nonce header. The same rule applies to @wordpress/i18n, which is why UI strings are translated server-side into state.config.i18n instead.

Registering a Custom Block

Use the evnm_blocks filter to register a third-party block. BlockRegistrar reads only the path key (a directory containing a valid block.json); there are no uses_store or view_data keys. A block inherits the shared Interactivity store simply by setting data-wp-interactive="eventonomy" in its markup, and reads server data on demand in render.php via evnm_get_view_data():

add_filter( 'evnm_blocks', function ( $blocks ) {
    $blocks['my-addon/event-video'] = [
        'path' => __DIR__ . '/build/event-video',
    ];
    return $blocks;
} );

In your block's render.php, read view data and wire Interactivity API attributes. evnm_get_view_data( 'event', $id ) returns a bundle [ 'event' => row, 'occurrences' => [...] ], so pull the row out before use:

$view  = evnm_get_view_data( 'event', $attributes['eventId'] ?? 0 );
$event = $view['event'] ?? [];
?>
<div
    data-wp-interactive="eventonomy"
    data-wp-context='<?php echo wp_json_encode( [ 'event' => $event ] ); ?>'
>
    <span data-wp-text="state.myCustomValue"></span>
    <button data-wp-on--click="actions.loadMyData">Load</button>
</div>

Template Overrides

Eventonomy renders its front end through blocks, not a set of standalone PHP page templates. There are three real override surfaces: copy the file into your theme (or child theme) under an eventonomy/ subdirectory. locate_template() resolves child-theme first, then parent theme, then the bundled file.

1. Block markup (the main surface)

Every dynamic block's render.php can be overridden per-block. Drop a file at {theme}/eventonomy/blocks/{slug}.php and it replaces that block's server render. Available slugs:

calendar, event-editor, events-list, manage-attendees, my-events, rsvp, search-filter, single-event, upcoming.

Pro contributes nine more through the same evnm_blocks seam, registered under the eventonomy-pro/ namespace: checkin, day-view, discovery-feed, follow-button, map-view, member-events, organizer-analytics, photo-grid, week-view. They are overridable the same way, at {theme}/eventonomy/blocks/{slug}.php.

Four Pro blocks are conditionally registered, so do not assume all eighteen exist. All nine Free blocks always register - Free has no per-block disable. Pro gates discovery-feed on the discovery feature flag, organizer-analytics on analytics, map-view on geocoding, and member-events on BuddyPress being active with the integration switched on. When a flag is off the block is not registered at all, so check WP_Block_Type_Registry::get_instance()->is_registered( 'eventonomy-pro/map-view' ) before depending on one. That is what Pro's own week/day views do before taking over the calendar slot.

For lighter tweaks that don't need a full re-render, filter the rendered HTML with evnm_block_output (( $html, $name, $attributes, $block ); add it with priority/arg-count 10, 4) instead of shipping an override file.

2. Email templates

Copy templates/emails/{relative}.php into {theme}/eventonomy/emails/{relative}.php to customize an email's HTML, e.g. emails/rsvp-confirmation.php, emails/magic-link.php, emails/organizer-notification.php, emails/parts/header.php, emails/parts/footer.php.

Pro: The custom email template builder in wp-admin (Pro) is an alternative to file-based overrides.

3. Classic-theme full-width page template

On classic themes, plugin pages render through templates/eventonomy-fullwidth-template.php; override it at {theme}/eventonomy/eventonomy-fullwidth-template.php, or opt out entirely with the evnm_use_fullwidth_template filter (return false). On block themes the plugin uses the theme's own page template, so this file does not apply.

Rules for safe overrides:

  • An override block render.php receives the same $attributes / $block the core render does; read event data via evnm_get_view_data(), never with a direct DB query.
  • Prefer the evnm_block_output and evnm_rest_prepare_event filters for data/markup tweaks; reserve full file overrides for structural changes.

Space scoping: the spaceId block attribute

The events-list and calendar blocks each expose a spaceId attribute (number, default 0) that scopes the block to a single space (community-group) id. When set, the block only renders events whose space_id matches; it hydrates through GET /events?space_id=… / the occurrences query, so no custom markup or query is involved. This is the seam the Pro BuddyPress group Events tab uses to mount a group-scoped events list or calendar. A spaceId of 0 (the default) is unscoped. Verified in src/blocks/events-list/block.json and src/blocks/calendar/block.json.

// Render a calendar scoped to space (group) 42.
echo do_blocks( '<!-- wp:eventonomy/calendar {"spaceId":42} /-->' );

Shortcodes

Seven of the nine Free blocks have a shortcode equivalent for classic-editor and page-builder use. Shortcodes call render_block() internally, so the output is byte-for-byte identical to placing the block.

Not every block has one. eventonomy/rsvp and eventonomy/manage-attendees register no shortcode - both are contextual surfaces that need the surrounding block context - and Pro registers no shortcodes at all for its nine blocks. Place those with the Block Editor, or render them yourself with do_blocks() as shown above.

Shortcode Block
[eventonomy_calendar view="month"] eventonomy/calendar
[eventonomy_events per_page="6" view="list" category="music"] eventonomy/events-list
[eventonomy_upcoming count="3"] eventonomy/upcoming
[eventonomy_event id="42"] eventonomy/single-event
[eventonomy_my_events] eventonomy/my-events
[eventonomy_search] eventonomy/search-filter
[eventonomy_submit event_id="0" redirect_url="/events/"] eventonomy/event-editor

What's Next?

Drive and test Eventonomy from the command line.

WP-CLI Commands →