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_blocksfilter
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-feedon thediscoveryfeature flag,organizer-analyticsonanalytics,map-viewongeocoding, andmember-eventson BuddyPress being active with the integration switched on. When a flag is off the block is not registered at all, so checkWP_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.phpreceives the same$attributes/$blockthe core render does; read event data viaevnm_get_view_data(), never with a direct DB query. - Prefer the
evnm_block_outputandevnm_rest_prepare_eventfilters 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.