• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » WordPress Development Recipe: Real-time custom event triggers using WebSockets and Heartbeat API

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_send which is hooked into heartbeat_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 $response array. This key is arbitrary but should be descriptive.
  • We also hook into heartbeat_settings to 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-tick event. This event fires whenever the Heartbeat AJAX request returns successfully.
  • Inside the heartbeat-tick handler, we check if the received data object 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, and heartbeat-error for comprehensive Heartbeat management.

Testing and Debugging

To test this setup:

  1. Activate the realtime-events plugin in your WordPress admin area.
  2. Navigate to any admin page (e.g., Dashboard).
  3. Open your browser’s developer console (usually by pressing F12).
  4. 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.
  5. 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-settings AJAX 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.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Step-by-Step Guide to building a custom database optimizer portal block for Gutenberg using Next.js headless configurations
  • Optimizing WooCommerce cart response times by lazy loading custom user transaction ledgers assets
  • Step-by-Step Guide: Offloading high-frequency custom subscription logs metadata writes to a Redis KV store
  • How to design a modular Command Query Responsibility Segregation (CQRS) architecture for enterprise-level custom plugins
  • Troubleshooting guide: Resolving memory leak spikes caused by unclosed custom database loops in user transaction ledgers

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (658)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (872)
  • PHP (5)
  • PHP Development (41)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (65)
  • WordPress Plugin Development (71)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • Step-by-Step Guide to building a custom database optimizer portal block for Gutenberg using Next.js headless configurations
  • Optimizing WooCommerce cart response times by lazy loading custom user transaction ledgers assets
  • Step-by-Step Guide: Offloading high-frequency custom subscription logs metadata writes to a Redis KV store

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (872)
  • Debugging & Troubleshooting (658)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala