How to build custom FSE Block Themes extensions utilizing modern Heartbeat API schemas
Leveraging the Heartbeat API for Advanced FSE Block Theme Extensions
The WordPress Heartbeat API, often perceived as a mechanism for real-time data synchronization, offers a powerful, albeit underutilized, avenue for extending Full Site Editing (FSE) block themes. By understanding its underlying architecture and modern schema, developers can craft sophisticated, dynamic functionalities that go beyond static theme options. This post delves into building custom extensions for FSE block themes by strategically employing the Heartbeat API, focusing on practical implementation with PHP and JavaScript.
Understanding the Heartbeat API’s Role in FSE
The Heartbeat API facilitates frequent, non-intrusive communication between the browser and the server. In the context of FSE, this translates to opportunities for:
- Real-time preview updates for dynamic theme elements.
- Synchronizing custom editor controls with theme settings.
- Implementing background tasks triggered by user activity within the editor.
- Fetching and displaying dynamic data within block previews.
Registering Custom Heartbeat Routes
To extend the Heartbeat API’s capabilities for your FSE theme, you need to register custom routes. This is achieved using the heartbeat_routes filter in PHP. These routes will act as endpoints for your JavaScript to send and receive data.
PHP Implementation for Route Registration
Create a PHP file within your theme’s functions directory (e.g., inc/heartbeat-extensions.php) and include it in your functions.php. The following code demonstrates how to register a new route named my_fse_theme_data.
<?php
/**
* Register custom Heartbeat API routes for FSE theme extensions.
*/
function my_fse_theme_register_heartbeat_routes( $routes ) {
$routes['my_fse_theme_data'] = 'my_fse_theme_handle_heartbeat_data';
return $routes;
}
add_filter( 'heartbeat_routes', 'my_fse_theme_register_heartbeat_routes' );
/**
* Handle Heartbeat API data for custom FSE theme extensions.
*
* @param array $response The Heartbeat API response.
* @param array $data The data sent from the client.
* @return array Modified response.
*/
function my_fse_theme_handle_heartbeat_data( $response, $data ) {
// Process incoming data if any
if ( isset( $data['my_custom_setting'] ) ) {
// Sanitize and save the setting, e.g., using update_option()
// For demonstration, we'll just echo it back.
$response['my_custom_setting_received'] = sanitize_text_field( $data['my_custom_setting'] );
}
// Add custom data to the response
$response['current_time'] = current_time( 'mysql' );
$response['site_title'] = get_bloginfo( 'name' );
return $response;
}
?>
In this example:
my_fse_theme_register_heartbeat_routesregisters a new routemy_fse_theme_data, which will be handled by themy_fse_theme_handle_heartbeat_datafunction.my_fse_theme_handle_heartbeat_datareceives the Heartbeat data. It can process incoming data (e.g., saving a custom theme setting) and adds its own data to the response (e.g., current server time and site title).
Client-Side JavaScript for Heartbeat Communication
On the client-side, you’ll enqueue a JavaScript file that utilizes the wp.heartbeat API. This script will send data to your registered route and process the response.
Enqueuing the JavaScript File
Add the following to your theme’s functions.php to enqueue your custom JavaScript file, ensuring it loads within the block editor.
<?php
/**
* Enqueue custom Heartbeat API JavaScript for the block editor.
*/
function my_fse_theme_enqueue_heartbeat_script() {
// Only load in the block editor.
if ( ! is_admin() && ! wp_is_block_editor() ) {
return;
}
wp_enqueue_script(
'my-fse-theme-heartbeat',
get_template_directory_uri() . '/js/heartbeat-extensions.js', // Path to your JS file
array( 'wp-heartbeat' ),
filemtime( get_template_directory() . '/js/heartbeat-extensions.js' ),
true // Load in footer
);
}
add_action( 'enqueue_block_editor_assets', 'my_fse_theme_enqueue_heartbeat_script' );
?>
JavaScript Implementation
Create a js/heartbeat-extensions.js file in your theme’s root directory. This script will interact with the Heartbeat API.
( function( window, document, wp, $ ) {
'use strict';
// Ensure wp.heartbeat is available
if ( ! wp || ! wp.heartbeat ) {
console.error( 'wp.heartbeat is not available.' );
return;
}
// Define the heartbeat interval (in milliseconds)
// WordPress default is 60000ms (60 seconds). Adjust as needed.
// Be mindful of server load and user experience.
var heartbeatInterval = 30000; // 30 seconds
// Initialize Heartbeat
wp.heartbeat.connect( {
interval: heartbeatInterval
} );
// Handle Heartbeat events
$( document ).on( 'heartbeat-send', function( e, data ) {
// Add custom data to be sent to the server
data.my_custom_setting = 'some_value_from_editor'; // Example: sending a value
data.action = 'my_fse_theme_data'; // Crucial: matches the registered PHP route key
} );
$( document ).on( 'heartbeat-tick', function( e, data ) {
// Process data received from the server
if ( data.my_custom_setting_received ) {
console.log( 'Server received custom setting:', data.my_custom_setting_received );
// Example: Update a UI element in the editor based on server response
// You might update a custom control's display or a status indicator.
}
if ( data.current_time ) {
console.log( 'Server time:', data.current_time );
// Example: Display server time in a custom editor panel
// $( '#custom-server-time-display' ).text( data.current_time );
}
if ( data.site_title ) {
console.log( 'Site Title:', data.site_title );
// Example: Use site title in a custom block's preview
}
} );
$( document ).on( 'heartbeat-connection-lost', function() {
console.warn( 'Heartbeat connection lost.' );
// Implement fallback or notification logic here
} );
$( document ).on( 'heartbeat-connection-restored', function() {
console.log( 'Heartbeat connection restored.' );
// Re-initialize or re-sync if necessary
} );
} )( window, document, wp, jQuery );
Key points in the JavaScript:
wp.heartbeat.connect()initiates the Heartbeat connection. Theintervalparameter controls how often the client sends data.- The
heartbeat-sendevent is triggered before data is sent. You add your custom data and importantly, theactionkey, which must match the route key registered in PHP (my_fse_theme_data). - The
heartbeat-tickevent fires when data is received from the server. You process thedataobject, which contains both your custom response data and any data added by WordPress core or other plugins. heartbeat-connection-lostandheartbeat-connection-restoredprovide hooks for managing connection states.
Practical FSE Extension Examples
1. Dynamic Block Preview Updates
Imagine a custom block that displays the site’s current visitor count, which is updated periodically on the server. The Heartbeat API can fetch this count and update the block’s preview in the editor.
Server-Side (PHP – Add to my_fse_theme_handle_heartbeat_data)
// ... inside my_fse_theme_handle_heartbeat_data function ...
// Fetch dynamic data (e.g., visitor count from a transient or database)
$visitor_count = get_transient( 'my_fse_theme_visitor_count' );
if ( false === $visitor_count ) {
// Simulate fetching or calculating visitor count
$visitor_count = rand( 100, 1000 );
set_transient( 'my_fse_theme_visitor_count', $visitor_count, 60 * 5 ); // Cache for 5 minutes
}
$response['visitor_count'] = $visitor_count;
// ... rest of the function ...
Client-Side (JavaScript – Add to heartbeat-extensions.js)
// ... inside $( document ).on( 'heartbeat-tick', ... ) ...
if ( data.visitor_count ) {
console.log( 'Current visitor count:', data.visitor_count );
// Assuming you have a way to target a specific block's preview or a custom UI element
// This is a conceptual example; actual implementation depends on block structure.
// For instance, if you have a custom block with an attribute 'visitorCountDisplay':
// wp.data.dispatch( 'core/editor' ).updateBlockAttributes( blockClientId, { visitorCountDisplay: data.visitor_count } );
// Or update a custom UI element:
// $( '#visitor-count-indicator' ).text( data.visitor_count );
}
// ... rest of the tick handler ...
2. Synchronizing Custom Editor Controls
If you’ve added custom meta boxes or controls outside the standard block editor UI that affect theme appearance, Heartbeat can help synchronize their state. For instance, a custom color picker in a theme options page that needs to reflect in the editor’s global styles.
Server-Side (PHP – Add to my_fse_theme_handle_heartbeat_data)
// ... inside my_fse_theme_handle_heartbeat_data function ...
// Get a custom theme option saved via theme settings or customizer
$primary_color = get_option( 'my_fse_theme_primary_color', '#0073aa' ); // Default WordPress blue
$response['primary_color'] = $primary_color;
// ... rest of the function ...
Client-Side (JavaScript – Add to heartbeat-extensions.js)
// ... inside $( document ).on( 'heartbeat-tick', ... ) ...
if ( data.primary_color ) {
console.log( 'Primary color from server:', data.primary_color );
// Update the editor's global styles or a custom UI element
// Example: Dispatching an action to update global styles (requires more complex integration)
// wp.data.dispatch( 'core/editor' ).updateEditorSettings( {
// colors: {
// primary: data.primary_color
// }
// } );
// Or update a custom UI element:
// $( '#primary-color-preview' ).css( 'background-color', data.primary_color );
}
// ... rest of the tick handler ...
Optimizing Heartbeat Usage
While powerful, the Heartbeat API can increase server load if not used judiciously. Consider these optimizations:
- Adjust the Interval: Use the shortest interval necessary. For non-critical updates, a longer interval (e.g., 60-120 seconds) is preferable.
- Conditional Data Sending: In the
heartbeat-sendevent, only include data that is actually needed for the current context or tick. - Debounce/Throttle: If your JavaScript logic involves rapid updates or user interactions, consider debouncing or throttling your Heartbeat send events to avoid overwhelming the server.
- Server-Side Logic: Keep server-side processing as efficient as possible. Cache data where appropriate.
- Targeted Enqueuing: Ensure your Heartbeat script only loads in contexts where it’s needed (e.g., the block editor, specific admin pages).
- Heartbeat Settings API: For more granular control over Heartbeat intervals and disabling it on specific pages, explore the
heartbeat_settingsfilter in PHP.
Controlling Heartbeat Intervals and Disabling
You can control the Heartbeat interval globally or on a per-page basis using the heartbeat_settings filter. This is crucial for performance tuning.
<?php
/**
* Control Heartbeat API settings.
*/
function my_fse_theme_heartbeat_settings( $settings ) {
// Example: Increase the default interval to 90 seconds globally
$settings['interval'] = 90000; // 90 seconds
// Example: Disable Heartbeat on specific admin pages if not needed
// global $pagenow;
// if ( 'edit.php' === $pagenow ) { // Example: Disable on post edit screen
// $settings['disabled'] = true;
// }
// You can also control specific actions
// $settings['php'] = array(
// 'my_fse_theme_data' => array( 'interval' => 30000 ), // Shorter interval for our custom action
// );
return $settings;
}
add_filter( 'heartbeat_settings', 'my_fse_theme_heartbeat_settings' );
?>
By carefully managing these settings, you can balance the dynamic capabilities offered by the Heartbeat API with the need for a performant and stable WordPress environment.
Conclusion
The Heartbeat API, when integrated thoughtfully, provides a robust framework for building advanced, dynamic extensions for FSE block themes. By mastering the registration of custom routes and implementing intelligent client-side JavaScript, developers can create richer editing experiences, real-time previews, and more responsive theme functionalities. Always prioritize performance by tuning Heartbeat intervals and minimizing unnecessary data transfer.