Plugins Personalizados en WordPress: Desarrollo de Bloques Gutenberg con React
El ecosistema de WordPress ha evolucionado de forma radical desde la introducción del editor de bloques (Gutenberg) en WordPress 5.0. Ya no basta con crear shortcodes o meta boxes tradicionales. Para 2025, el desarrollo de plugins personalizados en WordPress exige dominar React y la arquitectura de bloques nativa. Este artículo es una guía técnica, pensada para desarrolladores que quieren dar el salto a la creación de bloques Gutenberg personalizados con React, aprovechando al máximo el plugin development avanzado.
¿Por qué React para el desarrollo de bloques Gutenberg?
Gutenberg no es solo un editor visual; es un ecosistema completo basado en JavaScript moderno. El núcleo del editor está construido con React y la librería @wordpress/element. Al desarrollar bloques personalizados, no estás escribiendo PHP que renderiza HTML en el servidor (aunque eso también puede ocurrir). Estás creando componentes React que se ejecutan en el navegador del usuario.
Ventajas clave de usar React en WordPress:
- Interactividad real: Los bloques pueden responder a clics, cambios de estado y datos en tiempo real sin recargar la página.
- Componentes reutilizables: Puedes construir una biblioteca de componentes UI que se compartan entre varios bloques.
- Inspector de bloques: Acceso directo a los paneles de configuración (InspectorControls) que se sincronizan con el estado del bloque.
- API de datos (@wordpress/data): Gestiona el estado global del editor y la interacción con el REST API de WordPress de forma eficiente.
[INFO] Aunque el editor se ejecuta en el frontend con React, la persistencia de datos y la seguridad siguen dependiendo de PHP. El backend de WordPress (REST API) es el puente entre tu bloque React y la base de datos.
Configuración del entorno de desarrollo para 2025
Antes de escribir una sola línea de JSX, necesitas un entorno moderno. Olvídate de incluir React desde un CDN. El estándar para Gutenberg 2025 es usar @wordpress/scripts y un bundler como Webpack (ya preconfigurado).
Requisitos mínimos
- Node.js (versión 18 o superior, recomendada 20+).
- npm o yarn.
- Una instalación local de WordPress (LocalWP, Docker, etc.).
- Un plugin base donde alojarás tu bloque.
Inicialización de un plugin con soporte para bloques
Crea la estructura básica de tu plugin:
mi-plugin-personalizado/
├── src/
│ ├── blocks/
│ │ └── mi-primer-bloque/
│ │ ├── index.js
│ │ ├── edit.js
│ │ ├── save.js
│ │ ├── block.json
│ │ └── style.scss
│ └── index.js (punto de entrada del plugin)
├── build/ (se genera automáticamente)
├── mi-plugin-personalizado.php
└── package.json
Luego, instala las dependencias de desarrollo:
npm init -y
npm install @wordpress/scripts --save-dev
Añade el script de compilación a tu package.json:
"scripts": {
"build": "wp-scripts build",
"start": "wp-scripts start"
}
[TIP] El comando
wp-scripts startactiva el modo watch. Cada vez que guardes un archivo ensrc/, se recompilará automáticamente el bloque. Esencial para un flujo de trabajo ágil.
Anatomía de un bloque Gutenberg moderno con block.json
El archivo block.json es el corazón del bloque desde WordPress 5.8. Define metadatos, atributos, estilos y dependencias sin necesidad de registrarlo manualmente con PHP (aunque aún se requiere un register_block_type).
Ejemplo de block.json para un bloque de tarjeta de producto:
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "mi-plugin/tarjeta-producto",
"title": "Tarjeta de Producto",
"category": "widgets",
"icon": "cart",
"description": "Muestra una tarjeta con imagen, título y precio de un producto.",
"keywords": ["producto", "tarjeta", "ecommerce"],
"version": "1.0.0",
"textdomain": "mi-plugin",
"attributes": {
"productId": {
"type": "number",
"default": 0
},
"showPrice": {
"type": "boolean",
"default": true
}
},
"supports": {
"align": true,
"html": false,
"color": {
"background": true,
"text": true
}
},
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css"
}
Puntos clave del esquema:
apiVersion: 3: Es la versión más reciente y estable para 2025. Proporciona mejoras en el manejo de estilos y soporte para bloque anidados.attributes: Define el estado del bloque. Se serializan en el HTML de guardado y se restauran al editar.supports: Controla características nativas del editor (alineación, colores, tipografía). Esto reduce drásticamente la cantidad de código React que necesitas para opciones comunes.
Desarrollo del bloque React: edit.js y save.js
Aquí es donde ocurre la magia del plugin development avanzado. Vamos a crear el componente de edición y el de renderizado estático.
edit.js – El componente de edición (React)
Este componente se ejecuta dentro del editor de WordPress. Aquí defines la interfaz que verá el usuario mientras escribe.
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, TextControl, ToggleControl } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
export default function Edit({ attributes, setAttributes }) {
const { productId, showPrice } = attributes;
// Ejemplo de uso de la store de WordPress para obtener datos de un producto (CPT)
const product = useSelect((select) => {
if (!productId) return null;
const { getEntityRecord } = select('core');
return getEntityRecord('postType', 'product', productId);
}, [productId]);
const blockProps = useBlockProps();
return (
<div {...blockProps}>
<InspectorControls>
<PanelBody title={__('Configuración del Producto', 'mi-plugin')}>
<TextControl
label={__('ID del Producto', 'mi-plugin')}
value={productId}
onChange={(value) => setAttributes({ productId: parseInt(value, 10) || 0 })}
type="number"
/>
<ToggleControl
label={__('Mostrar precio', 'mi-plugin')}
checked={showPrice}
onChange={(value) => setAttributes({ showPrice: value })}
/>
</PanelBody>
</InspectorControls>
<div className="tarjeta-producto-preview">
{product ? (
<>
<h3>{product.title.rendered}</h3>
{showPrice && <p className="precio">{product.meta.precio}</p>}
</>
) : (
<p>{__('Selecciona un producto en la barra lateral.', 'mi-plugin')}</p>
)}
</div>
</div>
);
}
Detalles técnicos importantes:
useBlockProps: Es fundamental. Añade las clases y atributos necesarios para que el bloque funcione correctamente dentro del editor (resaltado, arrastre, etc.).InspectorControls: Renderiza los controles en la barra lateral derecha del editor.useSelect: Hook de React que se conecta con el almacén de datos de WordPress. Aquí obtenemos un registro de un Custom Post Type (producto) de forma reactiva.setAttributes: Es la única forma de modificar los atributos del bloque. No mutar directamente el objeto.
save.js – El componente de renderizado estático
Este componente define el HTML que se guarda en la base de datos y se muestra en el frontend. No tiene acceso a React ni a los hooks del editor.
import { useBlockProps } from '@wordpress/block-editor';
export default function save({ attributes }) {
const { productId, showPrice } = attributes;
const blockProps = useBlockProps.save();
// Nota: En este ejemplo simplificado, no renderizamos datos dinámicos aquí.
// Para datos dinámicos (CPT), es mejor usar render_callback en PHP.
return (
<div {...blockProps}>
<div className="tarjeta-producto-placeholder">
{/* El contenido real se renderizará desde PHP */}
<p>Producto ID: {productId}</p>
</div>
</div>
);
}
[WARNING] En
save.js, evita lógica compleja o llamadas a APIs. El HTML debe ser estático y predecible. Si tu bloque necesita datos dinámicos (como el precio de un producto que puede cambiar), es mejor que el frontend lo renderice mediante unrender_callbacken PHP y quesave.jssolo guarde un marcador de posición. Esto es una práctica avanzada para bloques React que interactúan con el backend.
Registro del bloque en PHP y renderizado dinámico
El archivo PHP de tu plugin debe registrar el bloque y, opcionalmente, proporcionar un callback para el renderizado en el frontend.
<?php
/**
* Plugin Name: Mi Plugin Personalizado
* Description: Bloques Gutenberg avanzados con React.
* Version: 1.0.0
* Requires at least: 6.0
* Requires PHP: 8.0
*/
function mi_plugin_register_block() {
// Registrar el bloque usando block.json
register_block_type( __DIR__ . '/build/blocks/tarjeta-producto' );
}
add_action( 'init', 'mi_plugin_register_block' );
// Renderizado dinámico para el frontend (opcional pero recomendado)
function mi_plugin_render_tarjeta_producto( $attributes, $content ) {
$product_id = isset( $attributes['productId'] ) ? intval( $attributes['productId'] ) : 0;
$show_price = isset( $attributes['showPrice'] ) ? boolval( $attributes['showPrice'] ) : true;
if ( ! $product_id ) {
return '<p>Producto no encontrado.</p>';
}
// Obtener datos del producto (ejemplo con un CPT 'product')
$product_title = get_the_title( $product_id );
$product_price = get_post_meta( $product_id, 'precio', true );
$output = '<div class="wp-block-mi-plugin-tarjeta-producto">';
$output .= '<h3>' . esc_html( $product_title ) . '</h3>';
if ( $show_price && $product_price ) {
$output .= '<p class="precio">' . esc_html( $product_price ) . ' €</p>';
}
$output .= '</div>';
return $output;
}
Luego, en tu block.json, añade:
"render_callback": "mi_plugin_render_tarjeta_producto"
Esto permite que el bloque se renderice correctamente incluso si JavaScript falla, y mantiene el contenido actualizado siempre.
Buenas prácticas para el desarrollo de bloques en 2025
El desarrollo de bloques Gutenberg personalizados ha madurado. Estas son las reglas no escritas que separan un plugin amateur de uno profesional.
1. Usa @wordpress/create-block para empezar
Aunque hemos creado la estructura manualmente, para proyectos reales usa:
npx @wordpress/create-block mi-bloque
Esto genera toda la configuración de Webpack, ESLint y el scaffolding del bloque.
2. Optimiza los estilos
Usa CSS Modules o SASS (ya soportado por @wordpress/scripts). Separa los estilos del editor (editor.scss) de los estilos del frontend (style.scss). El bloque aplicará editorStyle solo en el editor y style en ambos contextos.
3. Control de versiones y compatibilidad
Declara explícitamente en tu block.json la versión de WordPress mínima (requires) y la versión del bloque. Para Gutenberg 2025, apunta a "requires": "6.4" como mínimo para aprovechar todas las APIs.
4. Pruebas unitarias con Jest y @wordpress/scripts
Puedes testear tus componentes React usando el mismo entorno de pruebas que el núcleo de WordPress. Ejecuta npm run test:unit después de configurar Jest.
5. Internacionalización (i18n)
Usa siempre __(), _x() y sprintf() de @wordpress/i18n. No hardcodees cadenas de texto. Además, genera los archivos .pot con wp i18n make-pot.
Ejemplo avanzado: bloque que consume una API externa
Imagina un bloque que muestra el clima actual. Necesitarás un componente React que llame a una API en el editor, pero en el frontend, es mejor usar JavaScript asíncrono o un shortcode PHP.
En edit.js:
import { useState, useEffect } from '@wordpress/element';
import { Spinner } from '@wordpress/components';
// ... dentro del componente Edit
const [weather, setWeather] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (attributes.city) {
setLoading(true);
fetch(`https://api.openweathermap.org/data/2.5/weather?q=${attributes.city}&appid=TU_API_KEY`)
.then(res => res.json())
.then(data => {
setWeather(data);
setLoading(false);
});
}
}, [attributes.city]);
// Renderizar el clima o un spinner
En el frontend: Para no sobrecargar el servidor, el bloque puede guardar solo el nombre de la ciudad y, mediante un script frontend en JavaScript, hacer la llamada a la API cuando el usuario visite la página. Esto es más eficiente que renderizar desde PHP.
Conclusión: El futuro es Reactivo
El desarrollo de plugins personalizados en WordPress ya no es solo PHP. Para 2025, dominar React WordPress es un requisito indispensable para cualquier desarrollador que quiera crear experiencias de edición modernas y potentes. Los bloques Gutenberg personalizados te permiten extender el editor de forma nativa, con una interfaz de usuario rica y una arquitectura sólida.
Ya sea que estés construyendo un simple bloque de llamada a la acción o un complejo sistema de gestión de contenidos dentro del editor, el flujo de trabajo presentado aquí —basado en block.json, componentes React, y renderizado dinámico— es el estándar de la industria.
[TIP] No intentes abarcar todo de golpe. Empieza creando un bloque simple con un par de atributos y luego escala. La comunidad de WordPress y la documentación oficial (
developer.wordpress.org/block-editor) son excelentes recursos. El plugin development avanzado se construye paso a paso, bloque a bloque.
