Step-by-Step Guide to building a custom automated performance diagnostic log block for Gutenberg using Vanilla JS Web Components
Understanding the Need for Automated Performance Diagnostics
WordPress, while incredibly flexible, can become a performance bottleneck if not managed carefully. Theme and plugin bloat, inefficient database queries, and unoptimized assets are common culprits. While tools like Query Monitor are invaluable for manual debugging, integrating performance insights directly into the content creation workflow can empower editors and developers alike to make more informed decisions. This post outlines the construction of a custom Gutenberg block that leverages Vanilla JavaScript Web Components to display automated performance diagnostic data.
Project Setup and Core Concepts
We’ll be building a Gutenberg block that, when activated, will fetch and display key performance metrics. This involves understanding a few core WordPress and web development concepts:
- Gutenberg Blocks API: The foundation for building custom content elements in WordPress.
- Web Components: A set of web platform APIs that allow you to create new custom, reusable, encapsulated HTML tags to use in web pages and web apps. We’ll use this for the UI of our diagnostic display.
- JavaScript (Vanilla JS): For client-side logic, DOM manipulation, and API interaction.
- WordPress REST API: To fetch performance data from the server.
For this example, we’ll assume a basic WordPress development environment is set up. We’ll create a simple plugin to house our block. Navigate to your WordPress plugins directory and create a new folder, e.g., wp-performance-diagnostics. Inside this folder, create a main plugin file, wp-performance-diagnostics.php.
Plugin Registration and Block Registration
First, let’s register our plugin and enqueue our JavaScript file for the block editor. We’ll also register the block itself using register_block_type.
wp-performance-diagnostics.php
<?php
/**
* Plugin Name: WP Performance Diagnostics Block
* Description: A custom Gutenberg block to display automated performance diagnostics.
* Version: 1.0.0
* Author: Your Name
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Register the block.
*/
function wp_performance_diagnostics_register_block() {
// Automatically load dependencies and version.
$asset_file = include( plugin_dir_path( __FILE__ ) . 'build/index.asset.php');
register_block_type( 'wp-performance-diagnostics/diagnostics-block', array(
'api_version' => 2,
'editor_script' => 'wp-performance-diagnostics-editor-script',
'editor_style' => 'wp-performance-diagnostics-editor-style',
'style' => 'wp-performance-diagnostics-style',
'script' => 'wp-performance-diagnostics-script', // This will be our Web Component script
'attributes' => array(
'message' => array(
'type' => 'string',
'default' => 'Performance Diagnostics',
),
),
) );
}
add_action( 'init', 'wp_performance_diagnostics_register_block' );
/**
* Enqueue block editor assets.
*/
function wp_performance_diagnostics_editor_assets() {
wp_enqueue_script(
'wp-performance-diagnostics-editor-script',
plugins_url( 'build/index.js', __FILE__ ),
$asset_file['dependencies'],
$asset_file['version']
);
wp_enqueue_style(
'wp-performance-diagnostics-editor-style',
plugins_url( 'build/index.css', __FILE__ ),
array(),
filemtime( plugin_dir_path( __FILE__ ) . 'build/index.css' )
);
}
add_action( 'enqueue_block_editor_assets', 'wp_performance_diagnostics_editor_assets' );
/**
* Enqueue frontend assets.
*/
function wp_performance_diagnostics_frontend_assets() {
wp_enqueue_script(
'wp-performance-diagnostics-script',
plugins_url( 'build/web-component.js', __FILE__ ), // Our Web Component script
array(), // No dependencies for this standalone component
filemtime( plugin_dir_path( __FILE__ ) . 'build/web-component.js' ),
true // Load in footer
);
wp_enqueue_style(
'wp-performance-diagnostics-style',
plugins_url( 'build/style.css', __FILE__ ),
array(),
filemtime( plugin_dir_path( __FILE__ ) . 'build/style.css' )
);
}
add_action( 'wp_enqueue_scripts', 'wp_performance_diagnostics_frontend_assets' );
To manage JavaScript and CSS compilation, we’ll use a build process. A common setup involves @wordpress/scripts. Initialize a Node.js project in your plugin directory:
cd wp-performance-diagnostics npm init -y npm install @wordpress/scripts --save-dev
Add the following scripts to your package.json:
{
"name": "wp-performance-diagnostics",
"version": "1.0.0",
"description": "",
"main": "build/index.js",
"scripts": {
"build": "wp-scripts build",
"start": "wp-scripts start"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@wordpress/scripts": "^27.0.0"
}
}
Now, create a src directory within your plugin folder. Inside src, create index.js, index.css, and web-component.js. Also, create a style.css file at the root of your plugin directory.
Gutenberg Block Editor Implementation (src/index.js)
This file defines the block’s behavior within the Gutenberg editor. We’ll use @wordpress/blocks and @wordpress/element.
import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import { edit } from './edit'; // We'll create this component next
import { save } from './save'; // We'll create this component next
import './style.scss'; // For frontend styles
import './editor.scss'; // For editor styles
registerBlockType( 'wp-performance-diagnostics/diagnostics-block', {
title: __( 'Performance Diagnostics', 'wp-performance-diagnostics' ),
icon: 'performance', // WordPress dashicon
category: 'widgets',
attributes: {
message: {
type: 'string',
default: 'Performance Diagnostics',
},
},
edit: edit,
save: save,
} );
Editor Component (src/edit.js)
This component handles the block’s appearance and controls in the editor. For this diagnostic block, we’ll keep it simple, perhaps just displaying the title and a placeholder.
import { __ } from '@wordpress/i18n';
import { useBlockProps } from '@wordpress/block-editor';
import { TextControl } from '@wordpress/components';
export function edit( { attributes, setAttributes } ) {
const blockProps = useBlockProps();
const { message } = attributes;
return (
<div { ...blockProps }>
<h3>{ message }</h3>
<p>{ __( 'This block will display performance metrics when viewed on the frontend.', 'wp-performance-diagnostics' ) }</p>
<TextControl
label={ __( 'Block Title', 'wp-performance-diagnostics' ) }
value={ message }
onChange={ ( newMessage ) => setAttributes( { message: newMessage } ) }
/>
</div>
);
}
Save Component (src/save.js)
This component defines the static HTML that will be saved to the post content. Crucially, it will render our custom Web Component.
import { useBlockProps } from '@wordpress/block-editor';
export function save( { attributes } ) {
const blockProps = useBlockProps.save();
const { message } = attributes;
// We render our custom element here. The Web Component will handle its own rendering.
return (
<div { ...blockProps }>
<h3>{ message }</h3>
<performance-diagnostics-widget></performance-diagnostics-widget>
</div>
);
}
Web Component Implementation (src/web-component.js)
This is where the magic happens. We define a custom HTML element that will fetch and display performance data using Vanilla JavaScript. We’ll create a simple REST API endpoint to serve this data.
Backend: REST API Endpoint for Performance Data
Add this to your wp-performance-diagnostics.php file to create a REST API endpoint.
WP_REST_Server::READABLE,
'callback' => 'wp_performance_diagnostics_get_performance_data',
'permission_callback' => function() {
// Adjust permissions as needed. For simplicity, allow anyone.
// In production, you'd likely want to check user capabilities.
return true;
}
) );
}
add_action( 'rest_api_init', 'wp_performance_diagnostics_register_rest_route' );
/**
* Callback function to retrieve performance data.
*
* @param WP_REST_Request $request Full data.
* @return WP_REST_Response|\WP_Error Response object on success, or WP_Error object on failure.
*/
function wp_performance_diagnostics_get_performance_data( WP_REST_Request $request ) {
// In a real-world scenario, you'd gather metrics here.
// For demonstration, we'll return mock data.
$data = array(
'php_version' => phpversion(),
'memory_limit' => ini_get('memory_limit'),
'max_execution_time' => ini_get('max_execution_time'),
'wp_version' => get_bloginfo('version'),
'database_queries' => 'N/A (requires integration with query monitor or similar)', // Placeholder
'server_load' => 'N/A (requires server access)', // Placeholder
'cache_status' => 'Enabled' // Placeholder
);
return new WP_REST_Response( $data, 200 );
}
?>
src/web-component.js
class PerformanceDiagnosticsWidget extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' }); // Use Shadow DOM for encapsulation
this.shadowRoot.innerHTML = `
<style>
:host {
display: block;
border: 1px solid #ccc;
padding: 15px;
margin-top: 10px;
background-color: #f9f9f9;
font-family: sans-serif;
}
.metric {
margin-bottom: 8px;
}
.metric strong {
display: inline-block;
width: 150px; /* Align labels */
}
.loading {
font-style: italic;
color: #888;
}
.error {
color: red;
font-weight: bold;
}
</style>
<div id="diagnostics-container">
<p class="loading">Loading performance data...</p>
</div>
`;
this.container = this.shadowRoot.getElementById('diagnostics-container');
}
connectedCallback() {
this.fetchPerformanceData();
}
async fetchPerformanceData() {
try {
const response = await fetch('/wp-json/wp-performance-diagnostics/v1/performance-data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
this.renderData(data);
} catch (error) {
console.error('Error fetching performance data:', error);
this.renderError(error);
}
}
renderData(data) {
this.container.innerHTML = ''; // Clear loading message
for (const key in data) {
const metricElement = document.createElement('div');
metricElement.classList.add('metric');
metricElement.innerHTML = `<strong>${this.formatKey(key)}:</strong> ${data[key]}`;
this.container.appendChild(metricElement);
}
}
renderError(error) {
this.container.innerHTML = ''; // Clear loading message
const errorElement = document.createElement('p');
errorElement.classList.add('error');
errorElement.textContent = `Failed to load performance data: ${error.message}`;
this.container.appendChild(errorElement);
}
formatKey(key) {
// Basic formatting for display
return key.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase());
}
}
// Define the custom element if it hasn't been defined yet
if (!customElements.get('performance-diagnostics-widget')) {
customElements.define('performance-diagnostics-widget', PerformanceDiagnosticsWidget);
}
Styling (src/style.scss and src/editor.scss)
These files handle the styling for the block, both in the editor and on the frontend. For the Web Component, styles are encapsulated within its Shadow DOM.
src/style.scss (Frontend Styles)
.wp-block-wp-performance-diagnostics-diagnostics-block {
border: 1px dashed #0073aa;
padding: 15px;
margin-bottom: 20px;
background-color: #eaf4fa;
}
.wp-block-wp-performance-diagnostics-diagnostics-block h3 {
margin-top: 0;
color: #005177;
}
src/editor.scss (Editor Styles)
.wp-block-wp-performance-diagnostics-diagnostics-block {
border: 1px solid #0073aa;
padding: 15px;
background-color: #f0f8ff;
}
.wp-block-wp-performance-diagnostics-diagnostics-block h3 {
margin-top: 0;
color: #005177;
}
Build Process and Activation
Navigate to your plugin’s root directory in the terminal and run the build command:
npm run build
This command will compile your SCSS files and JavaScript into the build directory. After the build completes, activate the “WP Performance Diagnostics Block” plugin in your WordPress admin area. You should now be able to add the “Performance Diagnostics” block to your posts or pages.
Testing and Further Enhancements
Add the block to a page and view it on the frontend. The “Loading performance data…” message should be replaced with the actual metrics fetched from the REST API. This basic implementation can be extended significantly:
- Real-time Data: Integrate with server monitoring tools or WordPress plugins (like Query Monitor) to fetch more dynamic data (e.g., current database query count, memory usage).
- User Roles: Conditionally display the block or specific metrics based on the logged-in user’s role.
- Configuration: Allow users to select which metrics to display via block attributes.
- Error Handling: Implement more robust error reporting and fallback mechanisms.
- Performance Budgeting: Compare fetched metrics against predefined thresholds and visually indicate potential issues.
- Caching: Implement client-side caching for the fetched data to avoid repeated API calls on every page load.
- Server-Side Rendering (SSR): For critical performance data, consider rendering the initial metrics server-side to avoid the “flash” of loading content.
By combining the power of Gutenberg blocks with the encapsulation of Web Components and the flexibility of the WordPress REST API, you can create highly customized and informative tools directly within the WordPress content editor.