Step-by-Step Guide to building a custom automated performance diagnostic log block for Gutenberg using React components
Setting Up the WordPress Plugin and Development Environment
Before diving into React component development for Gutenberg, we need a robust WordPress plugin structure and a local development environment configured for modern JavaScript tooling. This involves setting up a basic plugin file, a `package.json` for dependency management, and a build process using tools like Webpack.
First, create a new directory for your plugin within the wp-content/plugins/ directory of your WordPress installation. Let’s name it performance-diagnostic-block.
Inside this directory, create the main plugin file, performance-diagnostic-block.php.
<?php
/**
* Plugin Name: Performance Diagnostic Block
* Description: A custom Gutenberg block to display performance diagnostic information.
* Version: 1.0.0
* Author: Your Name
* License: GPL-2.0-or-later
* Text Domain: performance-diagnostic-block
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Registers the block using the metadata loaded from the `block.json` file.
* Behind the scenes, it registers also all assets so they can be enqueued
* through the block editor in the corresponding context.
*
* @see https://developer.wordpress.org/reference/functions/register_block_type/
*/
function performance_diagnostic_block_init() {
register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'performance_diagnostic_block_init' );
Next, initialize npm in your plugin directory to manage JavaScript dependencies and build scripts.
cd wp-content/plugins/performance-diagnostic-block npm init -y
Now, install the necessary development dependencies. We’ll need React, ReactDOM, and the Gutenberg `@wordpress/scripts` package, which provides a pre-configured Webpack setup for WordPress development.
npm install --save-dev @wordpress/scripts react react-dom
In your package.json file, add the following scripts to handle the build process:
{
"name": "performance-diagnostic-block",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "wp-scripts build",
"start": "wp-scripts start"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@wordpress/scripts": "^26.10.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}
Create a block.json file in the root of your plugin directory. This file describes your block to WordPress and specifies its assets.
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "performance-diagnostic-block/diagnostic-info",
"version": "0.1.0",
"title": "Performance Diagnostic Info",
"category": "widgets",
"icon": "performance",
"description": "Displays key performance metrics for the current page.",
"keywords": [ "performance", "diagnostic", "speed", "metrics" ],
"attributes": {
"titleText": {
"type": "string",
"default": "Page Performance Metrics"
},
"showServerTime": {
"type": "boolean",
"default": true
},
"showDatabaseQueries": {
"type": "boolean",
"default": false
}
},
"textdomain": "performance-diagnostic-block",
"editorScript": "file:./build/index.js",
"editorStyle": "file:./build/index.css",
"style": "file:./build/style-index.css"
}
Finally, create a source directory (e.g., src/) for your JavaScript and CSS files. Inside src/, create index.js and edit.js.
mkdir src touch src/index.js src/edit.js
The src/index.js file will be the entry point for your block’s JavaScript. It registers the block and imports the necessary components for the editor and frontend.
import { registerBlockType } from '@wordpress/blocks';
import './style.scss'; // For frontend styles
import './editor.scss'; // For editor styles
import Edit from './edit';
import save from './save'; // We'll create this later
registerBlockType( 'performance-diagnostic-block/diagnostic-info', {
edit: Edit,
save,
} );
To build your assets, run the following command in your terminal:
npm run build
This command will compile your JavaScript and CSS into the build/ directory, which is referenced in block.json and registered by your PHP file.
Developing the Gutenberg Editor Component with React
The edit.js file is where you’ll define the React component that renders your block in the Gutenberg editor. This component will handle user interactions, attribute management, and the visual representation of the block while editing.
Let’s start by creating a basic structure for src/edit.js. We’ll use components from the @wordpress/block-editor and @wordpress/components packages.
import { __ } from '@wordpress/i18n';
import {
useBlockProps,
InspectorControls,
} from '@wordpress/block-editor';
import { PanelBody, TextControl, ToggleControl } from '@wordpress/components';
import './editor.scss';
export default function Edit( { attributes, setAttributes } ) {
const blockProps = useBlockProps();
const { titleText, showServerTime, showDatabaseQueries } = attributes;
const onChangeTitle = ( newTitle ) => {
setAttributes( { titleText: newTitle } );
};
const onToggleServerTime = ( isChecked ) => {
setAttributes( { showServerTime: isChecked } );
};
const onToggleDatabaseQueries = ( isChecked ) => {
setAttributes( { showDatabaseQueries: isChecked } );
};
return (
<>
<InspectorControls>
<PanelBody title={ __( 'Block Settings', 'performance-diagnostic-block' ) } initialOpen={ true }>
<TextControl
label={ __( 'Block Title', 'performance-diagnostic-block' ) }
value={ titleText }
onChange={ onChangeTitle }
/>
<ToggleControl
label={ __( 'Show Server Time', 'performance-diagnostic-block' ) }
checked={ showServerTime }
onChange={ onToggleServerTime }
/>
<ToggleControl
label={ __( 'Show Database Queries', 'performance-diagnostic-block' ) }
checked={ showDatabaseQueries }
onChange={ onToggleDatabaseQueries }
/>
</PanelBody>
</InspectorControls>
<div { ...blockProps }>
<h3>{ titleText }</h3>
<p>{ __( 'Editor Preview:', 'performance-diagnostic-block' ) }</p>
{ showServerTime && <p><strong>Server Time:</strong> (Preview)</p> }
{ showDatabaseQueries && <p><strong>Database Queries:</strong> (Preview)</p> }
<p>{ __( 'Configure block settings in the sidebar.', 'performance-diagnostic-block' ) }</p>
</div>
</>
);
}
In this edit.js component:
useBlockProps: A hook that provides necessary props for the block’s root element, including classes and attributes for proper editor integration.InspectorControls: A component that renders settings in the block sidebar (Inspector).PanelBody: A container for settings within the Inspector.TextControlandToggleControl: Input components for managing block attributes.attributesandsetAttributes: These are passed as props to the `Edit` component. `attributes` holds the current state of the block’s data, and `setAttributes` is a function to update that state.
We’ve defined three attributes in block.json: titleText (string), showServerTime (boolean), and showDatabaseQueries (boolean). The Edit component uses these attributes to render input fields in the sidebar and a preview within the editor.
After saving src/edit.js, run npm run build again to compile the changes. You should now be able to add your “Performance Diagnostic Info” block to a post or page in the WordPress editor and see the preview and sidebar controls.
Implementing the Frontend Save Component
The save function in your block’s JavaScript determines how the block is rendered on the frontend of your website. It should return JSX that represents the static HTML output of the block. For this block, we’ll create a src/save.js file.
import { useBlockProps } from '@wordpress/block-editor';
export default function save( { attributes } ) {
const blockProps = useBlockProps.save();
const { titleText, showServerTime, showDatabaseQueries } = attributes;
return (
<div { ...blockProps }>
<h3>{ titleText }</h3>
<ul>
{ showServerTime && <li><strong>Server Time:</strong> <span class="performance-data" data-metric="server-time">Loading...</span></li> }
{ showDatabaseQueries && <li><strong>Database Queries:</strong> <span class="performance-data" data-metric="db-queries">Loading...</span></li> }
</ul>
</div>
);
}
In src/save.js:
useBlockProps.save(): Similar touseBlockPropsin the editor, but specifically for the frontend save function.- The returned JSX includes the
titleTextand conditionally renders list items for server time and database queries if their respective attributes are true. - We’ve added placeholder
<span>elements with specific classes and data attributes (class="performance-data",data-metric="..."). These will be targeted by our JavaScript to dynamically populate the performance data.
Remember to update src/index.js to import and use this save function:
import { registerBlockType } from '@wordpress/blocks';
import './style.scss'; // For frontend styles
import './editor.scss'; // For editor styles
import Edit from './edit';
import save from './save'; // Import the save function
registerBlockType( 'performance-diagnostic-block/diagnostic-info', {
edit: Edit,
save, // Use the save function here
} );
Run npm run build again. Now, when you add the block to a page and publish it, the frontend will render the title and the selected metrics with “Loading…” placeholders.
Fetching and Displaying Performance Data with JavaScript
The final piece of the puzzle is to fetch the actual performance data and update the frontend. Since we’re dealing with server-side metrics, we’ll need a way to expose this data to the frontend JavaScript. A common and secure method is to use the WordPress REST API.
First, let’s create a REST API endpoint in your plugin’s PHP file to fetch the performance data. This endpoint will be responsible for gathering server time, database query counts, and potentially other metrics.
'GET',
'callback' => 'performance_diagnostic_block_get_metrics',
'permission_callback' => '__return_true', // Adjust permissions as needed for production
) );
}
add_action( 'rest_api_init', 'performance_diagnostic_block_register_rest_route' );
/**
* Callback function to get performance metrics.
*
* @return WP_REST_Response The response object.
*/
function performance_diagnostic_block_get_metrics() {
$metrics = array();
// Server Time (example: current server time)
$metrics['server_time'] = date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) );
// Database Queries (requires a plugin or custom query logging)
// For demonstration, we'll use a placeholder or a simple count if available.
// In a real-world scenario, you'd integrate with a query monitor plugin or WP_DEBUG_LOG.
global $wpdb;
if ( defined( 'SAVEQUERIES' ) && SAVEQUERIES ) {
$metrics['database_queries'] = count( $wpdb->queries );
} else {
// Fallback if SAVEQUERIES is not enabled.
// You might want to log this to a transient or option if needed for debugging.
$metrics['database_queries'] = 'SAVEQUERIES not enabled';
}
// Add more metrics here as needed (e.g., memory usage, execution time)
return new WP_REST_Response( $metrics, 200 );
}
Important Security Note: The 'permission_callback' => '__return_true' is for demonstration purposes. In a production environment, you should implement proper permission checks to ensure only authorized users can access this data. This might involve checking user capabilities (e.g., current_user_can( 'manage_options' )).
Now, let’s create a JavaScript file to fetch data from this endpoint and update the frontend. Create a new file src/frontend.js.
document.addEventListener( 'DOMContentLoaded', () => {
const performanceDataSpans = document.querySelectorAll( '.performance-data' );
if ( performanceDataSpans.length === 0 ) {
return; // No performance data spans found on this page
}
fetch( '/wp-json/performance-diagnostic-block/v1/metrics' )
.then( response => {
if ( ! response.ok ) {
throw new Error( `HTTP error! status: ${ response.status }` );
}
return response.json();
} )
.then( data => {
performanceDataSpans.forEach( span => {
const metricType = span.dataset.metric;
if ( data[metricType] !== undefined ) {
span.textContent = data[metricType];
} else {
span.textContent = 'N/A'; // Metric not available
}
} );
} )
.catch( error => {
console.error( 'Error fetching performance metrics:', error );
performanceDataSpans.forEach( span => {
span.textContent = 'Error loading data';
} );
} );
} );
This script:
- Waits for the DOM to be fully loaded.
- Selects all elements with the class
.performance-data. - Fetches data from our custom REST API endpoint
/wp-json/performance-diagnostic-block/v1/metrics. - Iterates through the fetched metrics and updates the
textContentof the corresponding spans based on theirdata-metricattribute. - Includes basic error handling.
We need to enqueue this script for the frontend. Create a file named style.scss and editor.scss in your src/ directory for basic styling, and then modify your src/index.js to include frontend.js.
// src/index.js
import { registerBlockType } from '@wordpress/blocks';
import './style.scss'; // For frontend styles
import './editor.scss'; // For editor styles
import './frontend.js'; // Import the frontend script
import Edit from './edit';
import save from './save';
registerBlockType( 'performance-diagnostic-block/diagnostic-info', {
edit: Edit,
save,
} );
Now, update your block.json to include the frontend script. The @wordpress/scripts build process will automatically handle enqueuing scripts and styles listed in block.json for the editor and frontend contexts.
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "performance-diagnostic-block/diagnostic-info",
"version": "0.1.0",
"title": "Performance Diagnostic Info",
"category": "widgets",
"icon": "performance",
"description": "Displays key performance metrics for the current page.",
"keywords": [ "performance", "diagnostic", "speed", "metrics" ],
"attributes": {
"titleText": {
"type": "string",
"default": "Page Performance Metrics"
},
"showServerTime": {
"type": "boolean",
"default": true
},
"showDatabaseQueries": {
"type": "boolean",
"default": false
}
},
"textdomain": "performance-diagnostic-block",
"editorScript": "file:./build/index.js",
"editorStyle": "file:./build/index.css",
"style": "file:./build/style-index.css",
"script": "file:./build/frontend.js"
}
Run npm run build one last time. After the build completes, activate your plugin in WordPress and visit a page where you’ve added the block. The “Loading…” placeholders should now be replaced with actual performance data fetched from your REST API endpoint.
Enhancements and Production Considerations
This setup provides a solid foundation. For production, consider these enhancements:
- Error Handling & Fallbacks: Implement more robust error handling in both PHP and JavaScript. Provide clear messages to the user if data cannot be fetched.
- Permissions: As mentioned, secure your REST API endpoint. Only allow access for users with appropriate roles (e.g., administrators, editors).
- Data Caching: For frequently accessed, less volatile data, consider caching the REST API response to reduce server load.
- Metric Selection: Allow users to select which specific metrics to display via the block’s attributes and UI.
- Performance Optimization: For very high-traffic sites, ensure your REST API endpoint is efficient. Avoid heavy database queries directly in the callback if possible; consider pre-calculating or caching metrics.
- Frontend Script Loading: Ensure
frontend.jsis only enqueued on pages where the block is actually used. Gutenberg’sblock.jsonhandles this automatically for the editor, but for the frontend, you might need conditional enqueuing in PHP if the block is not always present. - Internationalization: Ensure all user-facing strings are translatable using WordPress’s i18n functions (e.g.,
__(),_x()). - Styling: Add more comprehensive CSS for both the editor and frontend to ensure a polished look and feel.
By following these steps, you’ve built a custom, dynamic Gutenberg block that leverages React for the editor experience and fetches real-time data via the WordPress REST API for frontend display, offering a powerful tool for performance diagnostics directly within the WordPress admin.