WordPress Development Recipe: Real-time custom event triggers using WebSockets and Heartbeat API
Leveraging WordPress Heartbeat API for Real-time Event Triggers
WordPress’s built-in Heartbeat API provides a robust mechanism for real-time communication between the browser and the server. While often used for auto-saving and dashboard widgets, it can be powerfully repurposed to trigger custom events and push data to the client without requiring a full page reload. This recipe details how to set up a custom event system using Heartbeat, laying the groundwork for more advanced WebSocket integrations or simpler real-time notifications.
Server-Side: Registering a Custom Heartbeat Callback
The core of our server-side logic involves hooking into the Heartbeat API’s `heartbeat_send` filter. This filter allows us to inject custom data into the Heartbeat AJAX response. We’ll create a simple plugin to manage this.
First, create a new directory in wp-content/plugins/, for example, realtime-events. Inside this directory, create a main plugin file, realtime-events.php.
Plugin Header
Start with the standard WordPress plugin header:
/*
Plugin Name: Real-time Custom Events
Description: Triggers custom events via WordPress Heartbeat API.
Version: 1.0
Author: Antigravity
Author URI: https://example.com
*/
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
Registering the Heartbeat Callback
We’ll use the add_action function to hook into heartbeat_send. Our callback function will check for a specific condition (e.g., a custom flag set in user meta or a global state) and, if met, will add data to the Heartbeat response. This data will be our custom event payload.
/**
* Add custom data to the Heartbeat API response.
*
* @param array $response Heartbeat response data.
* @param array $data Heartbeat request data.
* @return array Modified Heartbeat response data.
*/
function realtime_events_heartbeat_send( $response, $data ) {
// Example: Check for a custom flag. In a real scenario, this could be
// based on user roles, specific post types, or a global state.
// For demonstration, we'll simulate an event trigger.
$trigger_event = false;
$event_data = array();
// Simulate an event trigger. This could be set by another process,
// a cron job, or an admin action.
// For this example, let's say we want to trigger an event every 30 seconds.
$last_trigger_time = get_transient( 'realtime_events_last_trigger' );
$current_time = time();
if ( ! $last_trigger_time || ( $current_time - $last_trigger_time > 30 ) ) {
$trigger_event = true;
$event_data = array(
'event_name' => 'new_notification',
'message' => 'A new important update is available!',
'timestamp' => $current_time,
);
set_transient( 'realtime_events_last_trigger', $current_time, 30 ); // Reset timer
}
if ( $trigger_event ) {
$response['realtime_events'] = $event_data;
}
return $response;
}
add_filter( 'heartbeat_send', 'realtime_events_heartbeat_send', 10, 2 );
/**
* Control Heartbeat frequency.
*
* @param array $settings Heartbeat settings.
* @return array Modified Heartbeat settings.
*/
function realtime_events_heartbeat_settings( $settings ) {
// Reduce the heartbeat interval to 15 seconds for more frequent checks.
// Default is 60 seconds. Adjust as needed for performance and responsiveness.
$settings['interval'] = 15;
return $settings;
}
add_filter( 'heartbeat_settings', 'realtime_events_heartbeat_settings' );
In this code:
- We define
realtime_events_heartbeat_sendwhich is hooked intoheartbeat_send. - Inside the callback, we simulate an event trigger. In a production environment, this logic would be more sophisticated, checking user meta, database flags, or external service statuses.
- We add our custom data under a unique key,
realtime_events, in the$responsearray. This key is arbitrary but should be descriptive. - We also hook into
heartbeat_settingsto reduce the Heartbeat interval to 15 seconds, making our custom events more responsive. Be mindful of server load when reducing this interval.
Client-Side: Listening for Heartbeat Events
On the client-side, WordPress provides the wp.heartbeat.connect() and wp.heartbeat.connection.bind() JavaScript functions to interact with the Heartbeat API. We need to enqueue a JavaScript file that listens for our custom event data.
Enqueuing the JavaScript File
Create a js directory within your plugin folder and add a file named realtime-events.js.
// Add the JavaScript file to the admin area.
function realtime_events_enqueue_scripts() {
// Only load on admin pages where Heartbeat is active.
if ( ! did_action( 'admin_enqueue_scripts' ) ) {
return;
}
// Enqueue our custom script.
wp_enqueue_script(
'realtime-events-js',
plugin_dir_url( __FILE__ ) . 'js/realtime-events.js',
array( 'jquery', 'heartbeat' ), // Dependencies: jQuery and Heartbeat API
filemtime( plugin_dir_path( __FILE__ ) . 'js/realtime-events.js' ),
true // Load in footer
);
}
add_action( 'admin_enqueue_scripts', 'realtime_events_enqueue_scripts' );
add_action( 'login_enqueue_scripts', 'realtime_events_enqueue_scripts' ); // For login screen if needed
add_action( 'wp_enqueue_scripts', 'realtime_events_enqueue_scripts' ); // For frontend if Heartbeat is enabled there
JavaScript Event Listener
Now, populate js/realtime-events.js with the following JavaScript code:
jQuery(document).ready(function($) {
// Ensure Heartbeat is available
if (typeof wp === 'undefined' || typeof wp.heartbeat === 'undefined') {
console.error('WordPress Heartbeat API not available.');
return;
}
// Bind to the heartbeat-send event
// This event fires just before Heartbeat sends data to the server,
// and also when it receives data back.
wp.heartbeat.connection.bind('heartbeat-send', function(e, data) {
// You can send custom data to the server here if needed, e.g.:
// data.realtime_events_client = { 'last_received_event_id': 123 };
});
wp.heartbeat.connection.bind('heartbeat-tick', function(e, data) {
// This event fires when Heartbeat receives data from the server.
// 'data' contains the response from the server.
// Check if our custom event data is present
if (data.hasOwnProperty('realtime_events')) {
var eventData = data.realtime_events;
console.log('Custom Heartbeat Event Received:', eventData);
// Trigger a custom jQuery event that other parts of your application can listen to.
// This decouples your event handling logic.
$(document).trigger('realtime_event_received', [eventData]);
// Example: Display a notification (requires a UI element)
if (eventData.event_name === 'new_notification') {
// Assuming you have a notification area with id="notification-area"
// $('#notification-area').html('<div class="notice notice-info">' + eventData.message + '</div>');
alert('Notification: ' + eventData.message); // Simple alert for demonstration
}
}
});
// You can also bind to heartbeat-ready and heartbeat-error
wp.heartbeat.connection.bind('heartbeat-ready', function() {
console.log('Heartbeat connection established.');
// You might want to send an initial payload here if needed
// wp.heartbeat.enqueue({ my_initial_data: 'hello' });
});
wp.heartbeat.connection.bind('heartbeat-error', function(e, error) {
console.error('Heartbeat error:', error);
});
// Start the Heartbeat connection if it's not already running.
// The Heartbeat API usually starts automatically on admin pages.
// If you need to ensure it starts, you can call:
// wp.heartbeat.connect();
// Example of listening to our custom triggered event
$(document).on('realtime_event_received', function(event, eventData) {
console.log('Global listener caught event:', eventData);
// Perform actions based on eventData
});
});
In this JavaScript:
- We wait for the DOM to be ready and check for the existence of
wp.heartbeat. - We bind to the
heartbeat-tickevent. This event fires whenever the Heartbeat AJAX request returns successfully. - Inside the
heartbeat-tickhandler, we check if the receiveddataobject contains our custom key,realtime_events. - If our custom data exists, we log it and then trigger a global jQuery event,
realtime_event_received. This is a best practice for decoupling your event handling logic, allowing other scripts to easily subscribe to these custom events without direct coupling to the Heartbeat implementation. - We also include example bindings for
heartbeat-send(to send data),heartbeat-ready, andheartbeat-errorfor comprehensive Heartbeat management.
Testing and Debugging
To test this setup:
- Activate the
realtime-eventsplugin in your WordPress admin area. - Navigate to any admin page (e.g., Dashboard).
- Open your browser’s developer console (usually by pressing F12).
- Observe the console logs. You should see “Heartbeat connection established.” and then periodically, “Custom Heartbeat Event Received:” followed by the JSON data we defined on the server.
- If you uncomment the
alert()line in the JavaScript, you’ll see a browser alert box.
If you don’t see the events:
- Ensure the plugin is active.
- Check the browser console for JavaScript errors.
- Verify that the Heartbeat API is enabled on the page. It’s typically active on admin pages by default. You can check for the presence of the
heartbeat-settingsAJAX action in your network tab. - Temporarily increase the Heartbeat interval in the PHP code (e.g., to 60 seconds) to see if the issue is related to excessive requests.
- Ensure your server environment isn’t blocking AJAX requests or interfering with WordPress core functionality.
Considerations for Production
While the Heartbeat API is convenient, it’s important to consider its limitations and implications for production environments:
- Performance: Frequent Heartbeat requests can increase server load. The default interval is 60 seconds. Reducing it to 15 seconds (as in the example) means 4 requests per minute per active user. For a high-traffic site, this can add up. Optimize the server-side logic to be as efficient as possible.
- Scope: Heartbeat is primarily designed for the WordPress admin area. While it can be enabled on the frontend, it requires explicit setup and careful consideration of its impact on user experience and performance.
- Scalability: For very high-volume, real-time needs (e.g., chat applications, live dashboards with thousands of concurrent users), a dedicated WebSocket server solution (like Socket.IO, Pusher, or a custom Node.js/Go server) will be more scalable and performant than relying solely on Heartbeat. The Heartbeat API can act as a bridge or a simpler notification system for less demanding scenarios.
- Security: Always sanitize and validate any data sent to the server via Heartbeat requests, and ensure sensitive data is not exposed in the response. Use nonces if you’re sending custom data from the client to the server.
- User Experience: Ensure that real-time updates enhance, rather than disrupt, the user experience. Provide clear visual cues for updates and allow users to control notifications if necessary.
Next Steps: Integrating with WebSockets
This Heartbeat-based system is an excellent stepping stone. For true, persistent, bi-directional real-time communication, you would typically integrate this with a WebSocket server. The Heartbeat API could then be used to:
- Server-Sent Events (SSE) Fallback: If a WebSocket connection fails or isn’t supported, Heartbeat can act as a fallback for pushing updates from the server.
- Connection Health Check: Use Heartbeat to periodically check if the WebSocket connection is still alive and re-establish it if necessary.
- Triggering WebSocket Events: Server-side logic that detects an event (e.g., a new comment) could trigger a Heartbeat response, which in turn, via the client-side JavaScript, sends a message to the WebSocket server to broadcast to other connected clients.
By mastering the WordPress Heartbeat API for custom event triggers, you gain a powerful tool for building more dynamic and responsive WordPress experiences, paving the way for more advanced real-time functionalities.