Step-by-Step Guide to building a custom REST API rate limiter block for Gutenberg using Tailwind CSS isolated elements
Setting Up Your WordPress Development Environment
Before diving into Gutenberg block development, ensure you have a robust local WordPress development environment. For this guide, we’ll assume you’re using a tool like Local by Flywheel, Docker with a WordPress image, or a custom LAMP/LEMP stack. The key is to have a functional WordPress installation where you can activate and test custom plugins without affecting a live site. You’ll also need Node.js and npm (or yarn) installed globally to manage the JavaScript build process for Gutenberg blocks.
Navigate to your WordPress installation’s plugin directory (typically wp-content/plugins/) and create a new directory for your custom block plugin. Let’s name it gutenberg-rest-api-rate-limiter.
Inside this new directory, create the main plugin file. This file will contain the plugin header and the necessary hooks to register your Gutenberg block.
Plugin Header and Block Registration
Create a file named gutenberg-rest-api-rate-limiter.php in your plugin directory and add the following plugin header and block registration code.
<?php
/**
* Plugin Name: Gutenberg REST API Rate Limiter Block
* Description: A custom Gutenberg block to display REST API rate limiting information.
* Version: 1.0.0
* Author: Your Name
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: gutenberg-rest-api-rate-limiter
*
* @package GutenbergRestApiRateLimiter
*/
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 gutenberg_rest_api_rate_limiter_block_init() {
register_block_type( __DIR__ . '/build' );
}
add_action( 'init', 'gutenberg_rest_api_rate_limiter_block_init' );
?>
This PHP file sets up the basic plugin information and uses the register_block_type function to register our block. The function points to a build directory, which will contain our compiled JavaScript and CSS assets. This is a standard practice for modern Gutenberg block development, leveraging tools like @wordpress/scripts.
Setting Up the JavaScript Build Process
To build our Gutenberg block, we’ll use the official WordPress scripts package, which simplifies the process of compiling JavaScript and CSS. First, navigate to your plugin’s root directory in your terminal:
cd wp-content/plugins/gutenberg-rest-api-rate-limiter
Initialize a package.json file if you don’t have one:
npm init -y
Now, install the necessary development dependencies:
npm install --save-dev @wordpress/scripts @wordpress/blocks @wordpress/components @wordpress/i18n @wordpress/element @wordpress/block-editor @wordpress/components @wordpress/server-side-render
Next, add build scripts to your package.json file. Open package.json and add the following lines within the scripts object:
{
"name": "gutenberg-rest-api-rate-limiter",
"version": "1.0.0",
"description": "A custom Gutenberg block to display REST API rate limiting information.",
"main": "build/index.js",
"scripts": {
"build": "wp-scripts build",
"start": "wp-scripts start",
"packages-update": "wp-scripts packages-update"
},
"keywords": ["wordpress", "gutenberg", "block"],
"author": "Your Name",
"license": "GPL-2.0-or-later",
"devDependencies": {
"@wordpress/block-editor": "^12.0.0",
"@wordpress/blocks": "^12.0.0",
"@wordpress/components": "^25.0.0",
"@wordpress/element": "^5.0.0",
"@wordpress/i18n": "^4.0.0",
"@wordpress/scripts": "^26.0.0",
"@wordpress/server-side-render": "^5.0.0"
}
}
The "build" script will compile your JavaScript and CSS into the build directory. The "start" script will watch for changes and automatically recompile, which is very useful during development.
Creating the Block’s Metadata (block.json)
Gutenberg blocks are defined using a block.json file. This file describes the block’s properties, including its name, title, category, and the assets it requires. Create a file named block.json in the root of your plugin directory.
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "gutenberg-rest-api-rate-limiter/block",
"version": "0.1.0",
"title": "REST API Rate Limiter Info",
"category": "widgets",
"icon": "shield-alt",
"description": "Displays information about REST API rate limiting.",
"keywords": ["rate limit", "api", "security"],
"attributes": {
"message": {
"type": "string",
"default": "API rate limits are currently within acceptable thresholds."
},
"warningThreshold": {
"type": "number",
"default": 80
},
"dangerThreshold": {
"type": "number",
"default": 95
}
},
"supports": {
"html": false
},
"textdomain": "gutenberg-rest-api-rate-limiter",
"editorScript": "file:./build/index.js",
"editorStyle": "file:./build/index.css",
"style": "file:./build/style-index.css"
}
This block.json defines:
name: A unique identifier for the block.title: The human-readable name shown in the block inserter.category: Where the block appears in the inserter.icon: The icon representing the block.description: A brief explanation of the block.attributes: Data that the block can store and manage (e.g., text messages, threshold values).editorScript: The main JavaScript file for the block editor.editorStyle: The CSS file for the block in the editor.style: The CSS file for the block on the front end.
Developing the Block’s JavaScript (Editor)
Create a new directory named src in your plugin’s root. Inside src, create index.js. This will be the entry point for your block’s JavaScript.
/**
* WordPress dependencies
*/
import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import {
PanelBody,
TextControl,
RangeControl,
Placeholder,
} from '@wordpress/components';
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
import ServerSideRender from '@wordpress/server-side-render';
/**
* Internal dependencies
*/
import './style.scss'; // For front-end styles
import './editor.scss'; // For editor styles
const blockDetails = {
title: __('REST API Rate Limiter Info', 'gutenberg-rest-api-rate-limiter'),
description: __('Displays information about REST API rate limiting.', 'gutenberg-rest-api-rate-limiter'),
icon: 'shield-alt',
category: 'widgets',
keywords: ['rate limit', 'api', 'security'],
};
const BlockEdit = ( { attributes, setAttributes } ) => {
const { message, warningThreshold, dangerThreshold } = attributes;
const blockProps = useBlockProps();
const onChangeMessage = ( newMessage ) => {
setAttributes( { message: newMessage } );
};
const onChangeWarningThreshold = ( newWarningThreshold ) => {
setAttributes( { warningThreshold: newWarningThreshold } );
};
const onChangeDangerThreshold = ( newDangerThreshold ) => {
setAttributes( { dangerThreshold: newDangerThreshold } );
};
return (
<div { ...blockProps }>
<InspectorControls>
<PanelBody title={ __( 'Rate Limiter Settings', 'gutenberg-rest-api-rate-limiter' ) }>
<TextControl
label={ __( 'Status Message', 'gutenberg-rest-api-rate-limiter' ) }
value={ message }
onChange={ onChangeMessage }
/>
<RangeControl
label={ __( 'Warning Threshold (%)', 'gutenberg-rest-api-rate-limiter' ) }
value={ warningThreshold }
onChange={ onChangeWarningThreshold }
min={ 0 }
max={ 100 }
step={ 1 }
/>
<RangeControl
label={ __( 'Danger Threshold (%)', 'gutenberg-rest-api-rate-limiter' ) }
value={ dangerThreshold }
onChange={ onChangeDangerThreshold }
min={ 0 }
max={ 100 }
step={ 1 }
/>
</PanelBody>
</InspectorControls>
<Placeholder
icon={ blockDetails.icon }
label={ blockDetails.title }
instructions={ __( 'This block displays REST API rate limiting status.', 'gutenberg-rest-api-rate-limiter' ) }
>
<p>{ message }</p>
<p>
{ __( 'Warning at:', 'gutenberg-rest-api-rate-limiter' ) } { warningThreshold }% | { __( 'Danger at:', 'gutenberg-rest-api-rate-limiter' ) } { dangerThreshold }%
</p>
<ServerSideRender
block="gutenberg-rest-api-rate-limiter/block"
attributes={ attributes }
/>
</Placeholder>
</div>
);
};
registerBlockType( 'gutenberg-rest-api-rate-limiter/block', {
edit: BlockEdit,
save: () => null, // We will render this block server-side
} );
In this index.js:
- We import necessary components from
@wordpress/blocks,@wordpress/i18n,@wordpress/components, and@wordpress/block-editor. registerBlockTypeis used to define our block.- The
BlockEditcomponent handles the block’s appearance and controls in the editor. InspectorControlsprovides a sidebar panel for users to configure the block’s attributes (message, warning, danger thresholds).Placeholderis used as a wrapper for the editor view, showing a preview and instructions.- Crucially, we use
ServerSideRender. This component tells Gutenberg to render the block on the server using a PHP callback, which is ideal for dynamic content like API status. - We set
save: () => nullbecause the block’s content will be entirely generated by the server-side rendering callback.
Styling the Block with Tailwind CSS (Isolated Elements)
To leverage Tailwind CSS for styling while keeping styles isolated to our block, we’ll use a combination of Tailwind’s utility classes directly in our JSX and a build process that generates a minimal CSS file. For this example, we’ll assume you have Tailwind CSS set up in your WordPress theme or a separate build process. If not, you’d typically install Tailwind CSS via npm and configure its PostCSS processor.
For this specific block, we’ll apply classes directly within the BlockEdit component and the server-side rendering callback. This approach ensures that only the styles needed for this block are included.
First, let’s add some basic styles to src/editor.scss and src/style.scss. These files will be processed by @wordpress/scripts.
/* src/editor.scss */
.wp-block-gutenberg-rest-api-rate-limiter-block {
border: 1px dashed #ccc;
padding: 15px;
background-color: #f9f9f9;
}
/* src/style.scss */
.wp-block-gutenberg-rest-api-rate-limiter-block {
border: 1px solid #ddd;
padding: 20px;
background-color: #fff;
border-radius: 4px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
Now, let’s modify the BlockEdit component to include some Tailwind utility classes for a more polished look in the editor. We’ll also add a placeholder for the actual rate limiting status, which will be rendered server-side.
// ... (previous imports) ...
const BlockEdit = ( { attributes, setAttributes } ) => {
const { message, warningThreshold, dangerThreshold } = attributes;
const blockProps = useBlockProps( {
className: 'my-custom-block-class', // Add a custom class for easier targeting
} );
const onChangeMessage = ( newMessage ) => {
setAttributes( { message: newMessage } );
};
const onChangeWarningThreshold = ( newWarningThreshold ) => {
setAttributes( { warningThreshold: newWarningThreshold } );
};
const onChangeDangerThreshold = ( newDangerThreshold ) => {
setAttributes( { dangerThreshold: newDangerThreshold } );
};
return (
<div { ...blockProps }>
<InspectorControls>
<PanelBody title={ __( 'Rate Limiter Settings', 'gutenberg-rest-api-rate-limiter' ) }>
<TextControl
label={ __( 'Status Message', 'gutenberg-rest-api-rate-limiter' ) }
value={ message }
onChange={ onChangeMessage }
help={ __( 'This message is displayed when the API is healthy.', 'gutenberg-rest-api-rate-limiter' ) }
/>
<RangeControl
label={ __( 'Warning Threshold (%)', 'gutenberg-rest-api-rate-limiter' ) }
value={ warningThreshold }
onChange={ onChangeWarningThreshold }
min={ 0 }
max={ 100 }
step={ 1 }
help={ __( 'Requests per minute threshold to trigger a warning.', 'gutenberg-rest-api-rate-limiter' ) }
/>
<RangeControl
label={ __( 'Danger Threshold (%)', 'gutenberg-rest-api-rate-limiter' ) }
value={ dangerThreshold }
onChange={ onChangeDangerThreshold }
min={ 0 }
max={ 100 }
step={ 1 }
help={ __( 'Requests per minute threshold to trigger a critical alert.', 'gutenberg-rest-api-rate-limiter' ) }
/>
</PanelBody>
</InspectorControls>
<div className="p-4 bg-gray-100 rounded-lg shadow-md">
<h3 className="text-lg font-semibold mb-2">{ __( 'API Rate Limiter Status', 'gutenberg-rest-api-rate-limiter' ) }</h3>
<p className="text-sm text-gray-700 mb-3">{ message }</p>
<div className="flex justify-between text-xs text-gray-600">
<span>{ __( 'Warning:', 'gutenberg-rest-api-rate-limiter' ) } { warningThreshold }%</span>
<span>{ __( 'Danger:', 'gutenberg-rest-api-rate-limiter' ) } { dangerThreshold }%</span>
</div>
</div>
{ /* Server-side rendering placeholder */ }
<div className="mt-4">
<ServerSideRender
block="gutenberg-rest-api-rate-limiter/block"
attributes={ attributes }
/>
</div>
</div>
);
};
// ... (registerBlockType call) ...
To make these Tailwind classes work, you need to ensure your WordPress theme or a separate build process is configured to compile Tailwind CSS. The @wordpress/scripts package can be configured to use PostCSS and Tailwind. You would typically have a postcss.config.js file and a tailwind.config.js file in your plugin’s root.
Example tailwind.config.js:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/**/*.js',
'./build/**/*.js',
'./**/*.php',
],
theme: {
extend: {},
},
plugins: [],
};
Example postcss.config.js:
module.exports = {
plugins: {
'tailwindcss/nesting': {},
tailwindcss: {},
autoprefixer: {},
},
};
After setting these up, you would run npm run build or npm run start. The @wordpress/scripts will pick up the PostCSS configuration and compile your SCSS files, including Tailwind directives, into the build directory.
Server-Side Rendering (PHP Callback)
Since we’re using ServerSideRender in the editor, we need a PHP callback function to render the block’s actual content on the front end and in the editor preview. This callback will fetch the current API rate limiting status and display it accordingly.
Modify your main plugin file (gutenberg-rest-api-rate-limiter.php) to include the render callback.
<?php
/**
* Plugin Name: Gutenberg REST API Rate Limiter Block
* Description: A custom Gutenberg block to display REST API rate limiting information.
* Version: 1.0.0
* Author: Your Name
* License: GPL-2.0-or-later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: gutenberg-rest-api-rate-limiter
*
* @package GutenbergRestApiRateLimiter
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Renders the REST API Rate Limiter block.
*
* @param array $attributes The block attributes.
* @return string The rendered HTML.
*/
function render_rest_api_rate_limiter_block( $attributes ) {
$message = isset( $attributes['message'] ) ? sanitize_text_field( $attributes['message'] ) : __( 'API rate limits are currently within acceptable thresholds.', 'gutenberg-rest-api-rate-limiter' );
$warning_threshold = isset( $attributes['warningThreshold'] ) ? intval( $attributes['warningThreshold'] ) : 80;
$danger_threshold = isset( $attributes['dangerThreshold'] ) ? intval( $attributes['dangerThreshold'] ) : 95;
// --- Placeholder for actual API Rate Limiting Logic ---
// In a real-world scenario, you would fetch actual rate limiting data here.
// This could involve:
// 1. Checking WordPress REST API response headers (e.g., X-RateLimit-Remaining, X-RateLimit-Limit).
// 2. Querying a custom database table or transient storing rate limit data.
// 3. Making an external API call to a monitoring service.
// For demonstration, we'll simulate a status.
$current_usage_percentage = rand( 0, 100 ); // Simulate current usage percentage
$status_class = 'text-green-600'; // Default to green
$status_text = __( 'OK', 'gutenberg-rest-api-rate-limiter' );
if ( $current_usage_percentage >= $danger_threshold ) {
$status_class = 'text-red-600 font-bold';
$status_text = __( 'CRITICAL', 'gutenberg-rest-api-rate-limiter' );
} elseif ( $current_usage_percentage >= $warning_threshold ) {
$status_class = 'text-yellow-600';
$status_text = __( 'WARNING', 'gutenberg-rest-api-rate-limiter' );
}
// --- End Placeholder ---
// Apply Tailwind CSS classes for styling
$wrapper_attributes = get_block_wrapper_attributes();
ob_start();
?>
<div { ...$wrapper_attributes }>
<div class="p-4 rounded-lg shadow-md flex items-center justify-between">
<div>
<p class="text-sm text-gray-700 mb-1"><strong>{ esc_html( $message ) }</strong></p>
<p class="text-xs text-gray-600">{ sprintf( __( 'Current Usage: %d%%', 'gutenberg-rest-api-rate-limiter' ), $current_usage_percentage ) }</p>
</div>
<div class="text-lg font-semibold { $status_class }">
{ esc_html( $status_text ) }
</div>
</div>
<div class="mt-3 text-xs text-gray-500">
{ sprintf( __( 'Warning threshold: %d%%, Danger threshold: %d%%', 'gutenberg-rest-api-rate-limiter' ), $warning_threshold, $danger_threshold ) }
</div>
</div>
<?php
return ob_get_clean();
}
/**
* 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 gutenberg_rest_api_rate_limiter_block_init() {
register_block_type( __DIR__ . '/build', array(
'render_callback' => 'render_rest_api_rate_limiter_block',
) );
}
add_action( 'init', 'gutenberg_rest_api_rate_limiter_block_init' );
?>
Key points in the render callback:
- It receives the block’s
$attributes. - It sanitizes and retrieves the attribute values.
- A placeholder section simulates fetching the current API rate limiting status. This is where you’d integrate your actual logic.
- Based on the simulated status and thresholds, it assigns CSS classes (e.g.,
text-green-600,text-yellow-600,text-red-600) for styling. get_block_wrapper_attributes()is used to apply necessary wrapper attributes, including the block’s unique class name (e.g.,wp-block-gutenberg-rest-api-rate-limiter-block).- The output is buffered using
ob_start()andob_get_clean()to return the generated HTML string. - The
render_callbackis passed toregister_block_type.
Building and Testing the Block
With all the files in place, it’s time to build the block assets. Open your terminal in the plugin’s root directory and run:
npm run build
This command will compile your JavaScript and SCSS files into the build directory. If you’re in development mode, you can use npm run start, which will watch for file changes and recompile automatically.
After building, activate the “Gutenberg REST API Rate Limiter Block” plugin in your WordPress admin area. Then, create or edit a post/page, open the block inserter, and search for “REST API Rate Limiter Info”. Add the block to your content.
You should see the block editor interface with the placeholder content and the Inspector Controls sidebar. You can adjust the message and thresholds there. When you switch to “Preview” mode or publish the post, the server-side rendering callback will execute, displaying the simulated rate limiting status.
Integrating Real Rate Limiting Data
The core of this block’s utility lies in fetching actual rate limiting data. Here are common strategies:
- WordPress REST API Headers: When WordPress makes requests to its own REST API (or external APIs via WP-CLI or other plugins), response headers often contain rate limit information (e.g.,
X-RateLimit-Remaining,X-RateLimit-Limit,X-RateLimit-Reset). You could potentially hook into the `rest_response_before_send` filter to capture this data and store it temporarily (e.g., in a transient). - Custom API Endpoints: If you have a custom API or a microservice managing rate limiting, you can create a dedicated WordPress REST API endpoint (using `register_rest_route`) that your block’s server-side rendering callback can query.
- Monitoring Services: Integrate with external monitoring tools (like Prometheus, Datadog, etc.) that track API usage. Your server-side callback would then query these services.
- Database/Transients: Implement a system within WordPress to track API calls. This could involve using WordPress transients or a custom database table to log requests and calculate current usage.
For example, to check the WordPress REST API’s own rate limiting headers (if enabled and configured), you might modify the render_rest_api_rate_limiter_block function:
// Inside render_rest_api_rate_limiter_block function...
// --- Placeholder for actual API Rate Limiting Logic ---
$current_usage_percentage = 0;
$status_class = 'text-green-600';
$status_text = __( 'OK', 'gutenberg-rest-api-rate-limiter' );
// Attempt to get rate limit info from WordPress REST API headers (if available)
// This is a simplified example and might require more robust error handling
// and checking if the REST API is actually being called.
$rest_api_headers = get_option( 'rest_api_rate_limits' ); // Example option, actual headers might be dynamic
if ( ! empty( $rest_api_headers ) && isset( $rest_api_headers['X-RateLimit-Remaining'] ) && isset( $rest_api_headers['X-RateLimit-Limit'] ) ) {
$remaining = intval( $rest_api_headers['X-RateLimit-Remaining'] );
$limit = intval( $rest_api_headers['X-RateLimit-Limit'] );
if ( $limit > 0 ) {
$current_usage_percentage = ( ( $limit - $remaining ) / $limit ) * 100;
}
} else {
// Fallback to simulated data if headers aren't available or configured
$current_usage_percentage = rand( 0, 100 );
}
// --- End Placeholder ---
// ... rest of the rendering logic ...
Remember to adapt the logic for fetching rate limiting data based on your specific infrastructure and requirements. The provided block structure gives you a flexible foundation to display this information effectively using Gutenberg and Tailwind CSS.