Durante años, para dominar el desarrollo de bloques en Gutenberg era necesario tener un conocimiento profundo de tecnologías como React y Node.js, además de seguir pasos de construcción complejos y utilizar herramientas de JavaScript.
Sin embargo, el desarrollo de WordPress está evolucionando, y ahora puedes crear y gestionar bloques de Gutenberg íntegramente en PHP.
Esto resulta especialmente útil para los desarrolladores que prefieren evitar React y el desarrollo con JavaScript (JS) del lado del servidor. Reduce la curva de aprendizaje, agiliza la experiencia del desarrollador y permite un mayor rendimiento al eliminar la sobrecarga innecesaria de los scripts del front end.
En las siguientes secciones, aprenderás a sacar partido a estas nuevas funcionalidades para crear bloques de Gutenberg solo con PHP. A lo largo del proceso, aprenderás a crear sitios web de WordPress más ligeros, rápidos y fáciles de mantener.
Muy emocionante, ¿verdad? Empecemos.
¿Qué son los bloques exclusivos de PHP y por qué son importantes?
Antes, para crear un bloque de Gutenberg se necesitaban conocimientos avanzados de JavaScript del lado del servidor y de programación con React. Esto supuso un obstáculo para que los desarrolladores veteranos de WordPress, que quizá no contaban con los conocimientos necesarios de React y Node.js.
Ahora las cosas están cambiando. A partir de Gutenberg 21.8, puedes registrar bloques Gutenberg utilizando únicamente PHP. Esto evita las complejidades de configurar un entorno Node.js para quienes no trabajan con JavaScript del lado del servidor.
Con el registro de bloques solo en PHP, puedes registrar y mostrar bloques tanto en el editor como en la interfaz pública utilizando el mismo código PHP. Esto anima a los sitios que usan temas híbridos o funciones PHP tradicionales y shortcodes a adoptar y desarrollar en el editor de bloques.
Para los que quieran saber más, aquí están las principales solicitudes de incorporación de cambios (PRs) de GitHub dedicadas a los bloques exclusivos de PHP.
- Permitir el registro de bloques sólo PHP: Este PR implementa el registro automático de bloques del lado del servidor y cambia el nombre del soporte
auto_ssraauto_register. - Bloques sólo PHP: Pasan todos los metadatos del registro PHP al cliente: Los bloques sólo PHP con soporte
auto_registerahora pasan todos los metadatos al cliente desde el registro PHP. - Bloques sólo PHP: Generar controles del inspector a partir de atributos: Este PR introduce la generación automática de la interfaz de usuario (controles del inspector) a partir de los atributos declarados en el servidor.
Cómo construir tu primer bloque Gutenberg sólo con PHP
Cuando un bloque se registra solo en el lado del servidor —sin archivos JS— y el nuevo indicador de compatibilidad auto_register está establecido en true, el editor utiliza automáticamente el componente ServerSideRender para registrar el bloque en el lado del cliente y mostrar la vista previa del bloque. En esencia, el contenido del bloque se genera ahora directamente a partir del código PHP tanto en el editor como en el frontend.
Para ilustrarlo, aquí tienes un sencillo ejemplo PHP que registra un bloque utilizando el método sólo PHP.
/**
* Render callback (frontend and editor)
*/
function my_php_only_block_render( $attributes ) {
return '<div>
<h3>🚀 PHP-only Block</h3>
<p>This block was created with only PHP!</p>
</div>';
}
/**
* Register the block on the 'init' hook.
*/
add_action( 'init', function() {
register_block_type( 'my-plugin/php-only-test-block', array(
'title' => 'My PHP-only Block',
'icon' => 'welcome-learn-more',
'category' => 'text',
'render_callback' => 'my_php_only_block_render',
'supports' => array(
// Automatically registers the block in the Editor JS (previously auto_ssr)
'auto_register' => true,
),
) );
});
Puedes probar este código copiándolo y pegándolo en el archivo principal de un plugin personalizado. Después de activar el plugin, deberías ver «Mi bloque sólo PHP» en el selector de bloques.

La función register_block_type registra un tipo de bloque en el servidor. Ahora incluye el nuevo soporte auto_register, que indica a Gutenberg que pase los metadatos del registro PHP.
La función acepta dos argumentos:
- El nombre del tipo de bloque, incluido el espacio de nombres. En este ejemplo, el nombre del bloque es
my-plugin/php-only-test-block. - Un array de parámetros para el tipo de bloque. En el código anterior, establecemos
title,icon,category,render_callbackysupports. De nuevo, para los tipos de bloque exclusivos de PHP, el arraysupportsdebe incluir“auto_register” => true.
Además de simplificar la creación de tipos de bloques personalizados y facilitar su integración en temas híbridos, los bloques solo PHP pueden utilizarse como envoltorios para funciones PHP heredadas y shortcodes. Por otra parte, el uso de bloques solo PHP abre la puerta a nuevas oportunidades para integraciones personalizadas y funcionalidades del lado del servidor.
Según Héctor Priethor
Un modelo de registro puramente PHP simplificaría los requisitos mínimos para el desarrollo de bloques, poniéndolos a disposición de un público más amplio de desarrolladores, y ayudaría a hacer crecer el ecosistema de bloques más allá del uso avanzado de JavaScript.
Uso de atributos para construir la interfaz de usuario de configuración de bloques
PR 74102 permite generar automáticamente controles de inspección a partir de las definiciones de atributos de los bloques. Esto permite a los usuarios configurar el aspecto y la funcionalidad de tus bloques exclusivos de PHP como si fueran cualquier bloque de Gutenberg registrado mediante JavaScript.
Antes, tenías que crear manualmente un archivo edit.js en React y definir varios controles de configuración mediante componentes React.
Ahora, Gutenberg lee las definiciones de atributos y genera automáticamente los campos de entrada correspondientes en el editor de WordPress.
El sistema mapea los tipos de datos definidos en el array de attributes a definiciones de campo DataForm.
'type' => 'string'genera un campo de texto.'type' => 'number'genera un campo numérico.'type' => 'integer'genera un campo entero.'type' => 'boolean'genera una casilla de verificación.'type' => 'string'con'enum' => array()genera un campo select.
Te darás cuenta de que sólo puedes utilizar unos pocos controles. Si necesitas controles específicos, como RichText, RangeControl o ToggleControl, tendrás que seguir optando por el enfoque JS/React.
Sin embargo, esta novedad tiene ventajas considerables. Se reducen aún más las barreras de entrada y no tendrás que aprender a usar React, Webpack o NPM para crear bloques personalizados con opciones sencillas de edición.
En el siguiente ejemplo, ampliamos el bloque de muestra mostrado en la sección anterior añadiendo algunos atributos.
/**
* 1. Define the block's HTML output.
*/
function my_php_only_block_render( $attributes ) {
// Extract attributes
$title = esc_html( $attributes['blockTitle'] );
$count = intval( $attributes['itemCount'] );
$enabled = $attributes['isEnabled']; // Boolean from the ToggleControl
$size = esc_attr( $attributes['displaySize'] );
// Start building the output
$output = sprintf( '<div class="my-php-block" style="font-size: %s; border: 1px solid #ccc; padding: 15px;">',
$size === 'large' ? '20px' : ($size === 'small' ? '12px' : '16px')
);
$output .= sprintf( '<h3>🚀 %s</h3>', $title );
// If the toggle is ON, show the list. If OFF, show a fallback message.
if ( $enabled ) {
$output .= '<ul>';
for ( $i = 1; $i <= $count; $i++ ) {
$output .= sprintf( '<li>Item %d</li>', $i );
}
$output .= '</ul>';
} else {
$output .= '<p><em>The list is currently disabled.</em></p>';
}
$output .= '</div>';
return $output;
}
/**
* 2. Register the block on 'init'.
*/
add_action( 'init', function() {
register_block_type( 'my-plugin/php-only-test-block', array(
'title' => 'My PHP-only Block',
'icon' => 'welcome-learn-more',
'category' => 'text',
'render_callback' => 'my_php_only_block_render',
// Attributes used to generate the Inspector UI
'attributes' => array(
'blockTitle' => array(
'type' => 'string',
'default' => 'PHP-only Block',
),
'itemCount' => array(
'type' => 'integer',
'default' => 3,
),
'isEnabled' => array(
'type' => 'boolean',
'default' => true,
),
'displaySize' => array(
'type' => 'string',
'enum' => array( 'small', 'medium', 'large' ),
'default' => 'medium',
),
),
'supports' => array(
'auto_register' => true,
),
) );
});
Un vistazo rápido a este código revela lo fácil que es registrar un bloque personalizado con todos sus ajustes de configuración utilizando la nueva API. Los atributos se utilizan ahora no sólo para almacenar los datos introducidos por el usuario, sino también para definir el esquema de la interfaz de usuario. El código anterior hace lo siguiente:
- La función
register_block_typeregistra el tipo de bloquemy-plugin/php-only-test-block. - El segundo argumento que se pasa a la función es un array que contiene los siguientes elementos:
title,icon,category,render_callback,attributesysupports. - El array de
atributoscontiene los atributos del bloque. En el ejemplo anterior, el array incluye los elementosblockTitle,itemCount,isEnabledydisplaySize. 'auto_register' => trueactiva el registro automático en el servidor.
Esto es lo que hace la función de devolución de llamada my_php_only_block_render:
- En primer lugar, la función extrae los valores de los atributos del array
$attributesy los asigna a las variables$title,$count,$enabledy$size. - A continuación, genera el contenido del bloque.
Aquí tienes el resultado en pantalla:

Un ejemplo real de bloques que solo usan PHP
Aunque hay muchos casos en los que JavaScript sigue siendo necesario, ya se pueden hacer muchas cosas solo con bloques de PHP, sobre todo si se usan junto con las propiedades de los bloques.
En el siguiente ejemplo, utilizamos la función get_block_wrapper_attributes(), que genera una cadena de atributos para el bloque actual que se está renderizando. El bloque recibirá automáticamente los colores, bordes y sombras establecidos por el usuario, y aplicará los estilos correspondientes al contenedor principal. De este modo, el bloque es personalizable a través de las herramientas nativas de Gutenberg, igual que un bloque basado en React.
Para verlo en funcionamiento, crea una carpeta smart-pricing-widget en tu ordenador. En esta carpeta, crea un archivo style.css con el siguiente código CSS:
/* style.css */
.tarjeta-precios {
display: flex;
flex-dirección: columna;
alinear-elementos: centro;
alineación-texto: centro;
box-sizing: border-box;
}
.pricing-card h3 {
margen: 0
font-size: 1.5rem;
}
.tarjeta-precio .precio-valor {
font-size: 3.5rem;
font-weight: 800;
margen: 15px 0;
}
.pricing-card ul {
estilo de lista: ninguno;
relleno: 25px 0
margen: 20px 0;
anchura: 100%;
borde superior: 1px sólido rgba(128,128,128,0.3);
mostrar: flex;
flex-dirección: columna;
separación: 12px;
}
.pricing-card li {
mostrar: flex;
alinear-elementos: centro;
justify-content: center;
separación: 10px;
}
.pricing-card .cta-button {
margin-top: auto;
relleno: 15px 25px;
radio del borde: 8px;
decoración del texto: ninguna;
font-weight: negrita;
transición: opacidad 0,2s;
}
.pricing-card .cta-button:hover {
opacidad: 0.8;
}
/* Variaciones temáticas */
.pricing-card.theme-light { color de fondo: #ffffff; color: #000000; }
.pricing-card.theme-light .cta-button { color-de-fondo: #21759b; color: #ffffff; }
.pricing-card.theme-dark { color de fondo: #1a1a1a; color: #ffffff; }
.pricing-card.theme-dark .cta-button { color-de-fondo: #ffffff; color: #1a1a1a; }
.pricing-card.theme-blue { color de fondo: #21759b; color: #ffffff; }
.pricing-card.theme-blue .cta-button { color-de-fondo: #000000; color: #ffffff; }
/* Clases de utilidad */
.pricing-card .is-full-width {
anchura: 100%;
display: block;
align-self: stretch;
}
No comentaremos este código, ya que se trata de una simple hoja de estilos para tu bloque de widgets.
Ahora, crea el archivo principal de tu plugin, llámalo smart-pricing-widget.php, y pega el siguiente código:
<?php
/**
* Plugin Name: Smart Pricing Widget
* Plugin URI: https://example.com/
* Description: PHP-only Pricing Table block
* Version: 1.2.0
* Author: Your Name
* Text Domain: smart-pricing-widget
*/
if ( ! defined( 'ABSPATH' ) ) exit;
/**
* Render callback
*/
function render_smart_pricing_block( $attributes ) {
$plan_name = esc_html( $attributes['planName'] );
$price = intval( $attributes['price'] );
$theme = $attributes['blockTheme'];
$btn_text = esc_html( $attributes['buttonText'] );
$btn_size = $attributes['buttonSize'];
$features_raw = $attributes['featuresList'];
$features_array = array_filter( array_map( 'trim', explode( ',', $features_raw ) ) );
$wrapper_attributes = wp_kses_data( get_block_wrapper_attributes( array(
'class' => "pricing-card theme-{$theme}",
) ) );
$output = sprintf( '<div %s>', $wrapper_attributes );
$output .= sprintf( '<h3>%s</h3>', $plan_name );
$output .= sprintf( '<div class="price-value">€%d</div>', $price );
if ( ! empty( $features_array ) ) {
$output .= '<ul>';
foreach ( $features_array as $feature ) {
$is_checked = strpos( $feature, '+' ) === 0;
$clean_text = esc_html( ltrim( $feature, '+- ' ) );
$icon = $is_checked ? '✅' : '❌';
$style = $is_checked ? '' : 'style="opacity: 0.6;"';
$output .= sprintf( '<li %s><span>%s</span> %s</li>', $style, $icon, $clean_text );
}
$output .= '</ul>';
}
$btn_class = 'cta-button' . ( $btn_size === 'full' ? ' is-full-width' : '' );
$output .= sprintf( '<a href="#" class="%s">%s</a>', esc_attr( $btn_class ), $btn_text );
$output .= '</div>';
return $output;
}
/**
* Register Assets and Block
*/
add_action( 'init', function() {
// 1. Register the CSS file
wp_register_style(
'smart-pricing-style',
plugins_url( 'style.css', __FILE__ ),
array(),
'1.2.0'
);
// 2. Register the Block
register_block_type( 'tutorial/smart-pricing', array(
'api_version' => 3,
'title' => 'Pricing Card',
'icon' => 'cart',
'category' => 'widgets',
'render_callback' => 'render_smart_pricing_block',
// Link the registered style handle here
'style' => 'smart-pricing-style',
'attributes' => array(
'planName' => array( 'type' => 'string', 'default' => 'Professional' ),
'price' => array( 'type' => 'integer', 'default' => 49 ),
'buttonText' => array( 'type' => 'string', 'default' => 'Choose Plan' ),
'buttonSize' => array( 'type' => 'string', 'enum' => array( 'auto', 'full' ), 'default' => 'auto' ),
'blockTheme' => array( 'type' => 'string', 'enum' => array( 'light', 'dark', 'blue' ), 'default' => 'light' ),
'featuresList' => array( 'type' => 'string', 'default' => "+ Support, + Updates, - Domain" ),
),
'supports' => array(
'auto_register' => true,
'color' => array( 'background' => true, 'text' => true ),
'spacing' => array( 'margin' => true, 'padding' => true ),
'typography' => array( 'fontSize' => true ),
'shadow' => true,
'__experimentalBorder' => array( 'color' => true, 'radius' => true, 'style' => true, 'width' => true ),
'border' => array( 'color' => true, 'radius' => true, 'style' => true, 'width' => true ),
),
) );
});
Este script incluye dos funciones. La función register_block_type() es el motor de tu plugin. Éstos son sus elementos principales:
- El primer argumento es el identificador del bloque, incluido el espacio de nombres (
tutorial/precios-inteligentes). - El segundo argumento es un array de argumentos. En el código anterior, hemos establecido la versión de la API, el título, el icono, la categoría, la llamada de retorno, el estilo, los atributos y los soportes.
- Los atributos del array generan los controles que utilizarán los usuarios para añadir contenido y configurar el bloque. El elemento
typeespecifica el tipo de control que se añadirá al inspector de bloques. En este ejemplo, son campos de texto ('type' => 'string'), un entero ('type' => 'integer'), y un par de menús desplegables ('type' => 'string', 'enum' => array()). - Los elementos del array
supportsañaden características que hacen que el estilo del bloque sea personalizable. Como hemos mencionado antes, el único soporte necesario en un bloque sólo PHP esauto_register, que permite la generación automática de la interfaz (UI) para atributos personalizados. Los otros soportes declarados anteriormente incluyen color, espaciado, tipografía, sombra y borde.
La función de devolución de llamada render_smart_pricing_block() genera el HTML del bloque. Aquí tienes una descripción detallada de lo que hace esta función:
- Extrae y sanea los atributos del bloque y, a continuación, añade el código CSS que genera la apariencia del bloque en el frontend y el editor.
- Las características que se mostrarán en el bloque (
$attributes['featuresList'];) se gestionan por separado. Actualmente, no es posible añadir controles avanzados a la barra lateral de configuración del bloque. Para crear una lista, como una lista de características, sólo puedes utilizar un simple campo de texto. En este ejemplo, debes introducir manualmente las características del producto, separadas por comas. - La variable
$wrapper_attributeses un contenedor para los atributos envolventes proporcionados por la funciónget_block_wrapper_attributes. Esta función no se limita a añadir las clases especificadas en el código (pricing-card theme-{$theme}), sino que recupera automáticamente todas las personalizaciones de estilo que el usuario establece en el inspector de bloques, incluidos los colores, los bordes, el relleno, el margen, la sombra, la tipografía y las clases de bloque estándar (wp-block-tutorial-smart-pricing). wp_kses_datagarantiza que no haya etiquetas o scripts maliciosos (XSS) en la cadena.- El resto del código genera el contenido del bloque.
Activa el plugin y crea una nueva entrada o página. Abre el insertador de bloques y desplázate hasta la sección Widgets. Aquí, deberías ver tu bloque «Pricing Card» identificado por un icono de carrito de la compra.

La imagen de arriba muestra el bloque con el tema light por defecto.
En la imagen inferior, puedes ver la versión oscura del bloque y los ajustes que has declarado en tu plugin.

La siguiente imagen muestra los controles de estilo que has añadido con los ajustes del bloque.

También cabe destacar que los estilos añadidos por los soportes anulan los estilos del tema del bloque. Esto permite una mayor personalización de la apariencia del bloque, como se muestra en la siguiente imagen:

Convierte shortcodes antiguos en bloques de Gutenberg con PHP puro
Uno de los usos más directos de los bloques PHP es como envoltorios de shortcodes. Con Gutenberg, puedes seguir utilizando shortcodes en tu contenido, pero tienes que insertar manualmente tu shortcode en un bloque Shortcode, lo que no es la experiencia más agradable.
Supongamos que tienes el siguiente shortcode
function my_custom_alert_shortcode( $atts ) {
$options = shortcode_atts( array(
'type' => 'info',
'message' => 'Default alert message',
), $atts );
$styles = array(
'info' => 'background: #d1ecf1; color: #0c5460; border-color: #bee5eb;',
'warning' => 'background: #fff3cd; color: #856404; border-color: #ffeeba;',
'error' => 'background: #f8d7da; color: #721c24; border-color: #f5c6cb;'
);
$style = $styles[ $options['type'] ] ?? $styles['info'];
return sprintf(
'<div class="sc-alert-box" style="%s padding: 20px; border: 1px solid; border-radius: 6px; margin: 10px 0;">
<strong style="text-transform: uppercase;">%s:</strong> %s
</div>',
esc_attr( $style ),
esc_html( $options['type'] ),
esc_html( $options['message'] )
);
}
add_shortcode( 'sc_alert', 'my_custom_alert_shortcode' );
Este código genera una sencilla caja que puedes insertar en tu contenido con el siguiente shortcode:
[sc_alert type="alert" message="Hello"]
En Gutenberg, utilizarás un bloque Shortcode para insertar la caja en tu contenido, como se muestra en la siguiente imagen:

La situación cambia por completo con los bloques exclusivos de PHP. Ahora puedes incluir tu shortcode en un bloque de Gutenberg exclusivo de PHP y configurarlo a través de los controles de la interfaz de usuario. Aquí tienes el código que debes añadir a tu plugin:
/**
* Rendering callback
*/
function render_shortcode_alert_wrapper_block( $attributes ) {
$type = esc_attr( $attributes['alertType'] );
$message = esc_attr( $attributes['alertMessage'] );
$shortcode_string = sprintf( '[sc_alert type="%s" message="%s"]', $type, $message );
$wrapper_attributes = wp_kses_data( get_block_wrapper_attributes( array(
'class' => 'wp-block-shortcode-alert-wrapper',
) ) );
return sprintf(
'<div %s>%s</div>',
$wrapper_attributes,
do_shortcode( $shortcode_string )
);
}
/**
* Register the block type on the server
*/
add_action( 'init', function() {
register_block_type( 'tutorial/alert-wrapper', array(
'api_version' => 3,
'title' => 'Alert (Shortcode wrapper)',
'icon' => 'feedback',
'category' => 'widgets',
'render_callback' => 'render_shortcode_alert_wrapper_block',
'attributes' => array(
'alertType' => array(
'type' => 'string',
'enum' => array( 'info', 'warning', 'error' ),
'default' => 'info',
),
'alertMessage' => array(
'type' => 'string',
'default' => 'Type your alert message here...',
),
),
'supports' => array(
'auto_register' => true,
'spacing' => array( 'margin' => true, 'padding' => true ),
'typography' => array( 'fontSize' => true ),
),
) );
});
El código anterior es similar al de la sección anterior. Lo que cambia aquí es el callback de renderizado.
$shortcode_stringalmacena la cadena del shortcode ([sc_alert type="%s" message="%s"]).- La función devuelve el HTML del bloque contenedor y el shortcode incluido (
do_shortcode( $shortcode_string )).
Ahora, abre el insertador de bloques y busca el bloque «Shortcode wrapper» entre los widgets. Insértalo en tu contenido y configúralo desde la barra de ajustes del bloque. El bloque aparecerá idéntico tanto en el editor como en el frontend.

¿Cómo está cambiando el desarrollo de WordPress con los bloques sólo PHP?
Tal y como están las cosas, los bloques de PHP puro se encuentran en fase experimental y aún tienen capacidades limitadas. Gutenberg ofrece funcionalidades más potentes, como patrones de bloques y variaciones de bloques, que proporcionan todas las funcionalidades de edición de los bloques nativos de Gutenberg y de los bloques personalizados creados en JavaScript. Sin embargo, hay situaciones en las que los bloques de PHP ofrecen oportunidades importantes.
En primer lugar, los bloques exclusivos para PHP deberían fomentar una mayor adopción del editor de bloques, sobre todo entre los desarrolladores de WordPress menos familiarizados con el desarrollo de JavaScript del lado del servidor.
Además, son envoltorios ideales para funciones personalizadas y shortcodes, como se muestra en el ejemplo de este artículo. Por otra parte, permiten una fácil integración con servicios externos.
Y también podemos esperar, de forma razonable, mejoras futuras y nuevas funcionalidades, más opciones de configuración e integraciones con las herramientas de Gutenberg ya existentes.
Una cosa está clara: con los bloques solo para PHP, crear bloques de Gutenberg se ha vuelto mucho más fácil.
Si el desarrollo de WordPress es tu trabajo, Kinsta te proporciona las herramientas para desarrolladores que necesitas, permitiéndote centrarte en el desarrollo de WordPress, eliminando la necesidad de complejas configuraciones y tediosas tareas de mantenimiento: SSH, SFTP, integración Git, actualizaciones automáticas, staging con un solo clic, una herramienta de desarrollo local integrada y mucho más. Pruébalo de primera mano con tu primer mes gratis.