Entorno de desarrollo de bloques dinámicos Gutenberg en WordPress con React, inspector de atributos e integración de datos remotos vía API REST
WordPress

Cómo Crear Bloques Dinámicos en Gutenberg (WordPress) que Consumen APIs REST Externas con React y Server-Side Rendering

Aprende a desarrollar bloques personalizados para el editor de bloques de WordPress (Gutenberg) utilizando React, block.json v3 y renderizado dinámico en servidor con caché de transitorios.

El editor de bloques de WordPress (Gutenberg) transformó de raíz la experiencia editorial en la web, dejando atrás el clásico editor TinyMCE y los pesados constructores visuales (page builders) basados en shortcodes monolíticos. Construido íntegramente sobre React, Gutenberg permite componer páginas complejas combinando interfaces declarativas y estructuras semánticas modulares.

Sin embargo, cuando una empresa necesita mostrar datos en tiempo real dentro de sus publicaciones —como tasas de cambio de divisas, cotizaciones de criptomonedas, disponibilidad de inventario de un ERP o festivos bancarios oficiales— muchos desarrolladores cometen un grave error de arquitectura: guardar los datos de la API de forma estática en la base de datos de WordPress.

Si un bloque guarda la respuesta JSON de una API directamente en el HTML de post_content, la información quedará congelada en el tiempo. Para que los datos se actualicen cuando la fuente remota cambie, es indispensable implementar Bloques Dinámicos con Server-Side Rendering (SSR).

En este artículo técnico para desarrolladores de WordPress e ingenieros frontend, construiremos paso a paso un bloque dinámico para Gutenberg con @wordpress/scripts, exploraremos el ciclo de vida de React en el editor (edit.js), implementaremos el renderizado en servidor en PHP (render.php) y protegeremos el rendimiento del sitio mediante la API de Transitorios (Transients API).


1. Bloques Estáticos vs. Bloques Dinámicos: La Decisión Arquitectónica

Gutenberg maneja dos modelos de persistencia y visualización para los bloques:

DimensiónBloque EstáticoBloque Dinámico (SSR)
PersistenciaGuarda el marcado HTML final renderizado dentro de la columna post_content de la tabla wp_posts.Solo guarda los atributos en un comentario delimitador JSON (<!-- wp:mi-plugin/mi-bloque {"apiKey":"..."} /-->).
Renderizado FrontendWordPress sirve el HTML estático directamente desde la base de datos sin ejecutar PHP en cada visita.WordPress ejecuta una función PHP (render_callback o render.php) en tiempo de ejecución para generar el marcado.
Casos de UsoPárrafos, títulos, banners estáticos, listas fijas de beneficios.Consumo de APIs REST externas, listados de entradas recientes, carritos de compra, paneles de precios en vivo.
Riesgo de InvalidaciónSi cambias la estructura HTML de la función save(), el editor muestra el temido error “Este bloque contiene contenido inesperado o no válido”.Cero riesgo de invalidación de marcado, ya que la función save() retorna null y el HTML se genera dinámicamente en servidor.

💡 Regla de Oro: Si el bloque depende de datos externos que cambian sin intervención manual del redactor en el panel de administración, el bloque DEBE ser dinámico. La función save de React debe retornar null.


2. Configuración del Entorno con @wordpress/scripts

La forma estándar y recomendada por el equipo de WordPress Core para desarrollar bloques modernos es utilizar el paquete oficial @wordpress/scripts, que encapsula Webpack, Babel, PostCSS y ESLint sin necesidad de configuraciones manuales complejas:

# Inicializar el proyecto dentro del directorio wp-content/plugins/
npx @wordpress/create-block doneapi-live-rates \
  --template @wordpress/create-block/template-esnext \
  --no-plugin

La estructura resultante dentro del plugin será:

doneapi-live-rates/
├── block.json          # Metadatos del bloque (Schema v3)
├── src/
│   ├── edit.js         # Componente React para el editor de WordPress
│   ├── index.js        # Registro del bloque en el cliente
│   ├── render.php      # Renderizado dinámico en servidor (PHP)
│   ├── style.scss      # Estilos compartidos (frontend y backend)
│   └── editor.scss     # Estilos exclusivos del panel de administración
├── build/              # Archivos compilados listos para producción
└── doneapi-plugin.php  # Archivo principal de registro del plugin

3. Definición del Manifiesto block.json (Schema v3)

El archivo block.json es la fuente única de verdad para el bloque. Centraliza los atributos, dependencias y scripts, permitiendo que WordPress cargue los recursos de forma diferida (asset loading on-demand):

{
  "$schema": "https://schemas.wp.org/trunk/block.json",
  "apiVersion": 3,
  "name": "doneapi/live-rates",
  "version": "1.0.0",
  "title": "DoneAPI Cotizaciones en Vivo",
  "category": "widgets",
  "icon": "chart-line",
  "description": "Muestra tasas de cambio e indicadores financieros actualizados vía API REST en tiempo real.",
  "supports": {
    "html": false,
    "align": ["wide", "full"],
    "color": {
      "background": true,
      "text": true
    },
    "spacing": {
      "margin": true,
      "padding": true
    }
  },
  "attributes": {
    "baseCurrency": {
      "type": "string",
      "default": "USD"
    },
    "targetCurrency": {
      "type": "string",
      "default": "COP"
    },
    "refreshInterval": {
      "type": "number",
      "default": 3600
    },
    "cardTitle": {
      "type": "string",
      "default": "Tipo de Cambio Oficial"
    }
  },
  "textdomain": "doneapi-rates",
  "editorScript": "file:./build/index.js",
  "editorStyle": "file:./build/index.css",
  "style": "file:./build/style-index.css",
  "render": "file:./render.php"
}

4. Desarrollo de la Interfaz del Editor con React (src/edit.js)

En el panel de administración, el redactor debe poder previsualizar cómo lucirán los datos de la API y personalizar los atributos del bloque a través del Inspector lateral (Sidebar Controls).

Utilizamos los componentes del ecosistema @wordpress/components y el hook useBlockProps:

import { __ } from '@wordpress/i18n';
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, SelectControl, TextControl, Spinner } from '@wordpress/components';
import { useState, useEffect } from '@wordpress/element';

export default function Edit({ attributes, setAttributes }) {
  const { baseCurrency, targetCurrency, cardTitle } = attributes;
  const blockProps = useBlockProps({
    className: 'doneapi-rates-block-editor',
  });

  const [rateData, setRateData] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  // Consulta mock o directa en el editor para previsualización inmediata
  useEffect(() => {
    let isMounted = true;
    setIsLoading(true);
    setError(null);

    // Consulta al endpoint público de utilidades o mock local
    fetch(`https://api.doneapi.com/v1/finance/rates?base=${baseCurrency}&target=${targetCurrency}`)
      .then((res) => {
        if (!res.ok) throw new Error(__('Error al consultar el servicio', 'doneapi-rates'));
        return res.json();
      })
      .then((data) => {
        if (isMounted) {
          setRateData(data);
          setIsLoading(false);
        }
      })
      .catch((err) => {
        if (isMounted) {
          setError(err.message);
          setIsLoading(false);
        }
      });

    return () => {
      isMounted = false;
    };
  }, [baseCurrency, targetCurrency]);

  return (
    <>
      <InspectorControls>
        <PanelBody title={__('Configuración de la API', 'doneapi-rates')} initialOpen={true}>
          <TextControl
            label={__('Título del Bloque', 'doneapi-rates')}
            value={cardTitle}
            onChange={(val) => setAttributes({ cardTitle: val })}
          />
          <SelectControl
            label={__('Moneda Base', 'doneapi-rates')}
            value={baseCurrency}
            options={[
              { label: 'USD - Dólar Estadounidense', value: 'USD' },
              { label: 'EUR - Euro', value: 'EUR' },
            ]}
            onChange={(val) => setAttributes({ baseCurrency: val })}
          />
          <SelectControl
            label={__('Moneda Destino', 'doneapi-rates')}
            value={targetCurrency}
            options={[
              { label: 'COP - Peso Colombiano', value: 'COP' },
              { label: 'MXN - Peso Mexicano', value: 'MXN' },
              { label: 'BRL - Real Brasileño', value: 'BRL' },
              { label: 'CLP - Peso Chileno', value: 'CLP' },
            ]}
            onChange={(val) => setAttributes({ targetCurrency: val })}
          />
        </PanelBody>
      </InspectorControls>

      <div {...blockProps}>
        <div className="doneapi-rates-card">
          <div className="doneapi-rates-header">
            <h4>{cardTitle || __('Cotización en Vivo', 'doneapi-rates')}</h4>
            <span className="doneapi-badge">Live API</span>
          </div>

          {isLoading && (
            <div className="doneapi-loading-state">
              <Spinner />
              <p>{__('Consultando API remota...', 'doneapi-rates')}</p>
            </div>
          )}

          {error && (
            <div className="doneapi-error-notice">
              <p>⚠️ {error}</p>
              <small>{__('Modifica los parámetros en la barra lateral', 'doneapi-rates')}</small>
            </div>
          )}

          {!isLoading && !error && rateData && (
            <div className="doneapi-rates-content">
              <div className="doneapi-rate-figure">
                <span className="currency-pair">{baseCurrency}/{targetCurrency}</span>
                <span className="rate-value">${Number(rateData.rate).toLocaleString()}</span>
              </div>
              <p className="last-updated">
                {__('Actualizado:', 'doneapi-rates')} {new Date(rateData.timestamp).toLocaleTimeString()}
              </p>
            </div>
          )}
        </div>
      </div>
    </>
  );
}

5. El Renderizado en Servidor con Caché de Transitorios (render.php)

Este es el archivo más importante de la solución. Cuando un visitante abre una entrada en el sitio web público, WordPress ejecuta render.php.

Si hiciéramos una llamada HTTP wp_remote_get() en cada visita, 10,000 visitas simultáneas dispararían 10,000 peticiones a la API externa, agotando la cuota del servicio (rate limit) y colapsando el tiempo de respuesta del servidor web. La solución arquitectónica obligatoria es la API de Transitorios de WordPress:

<?php
/**
 * Renderizado en Servidor para el bloque doneapi/live-rates
 *
 * @param array    $attributes Atributos guardados del bloque.
 * @param string   $content    Contenido interno del bloque.
 * @param WP_Block $block      Instancia del bloque procesado.
 */

if (!defined('ABSPATH')) {
    exit;
}

$base_currency   = sanitize_text_field($attributes['baseCurrency'] ?? 'USD');
$target_currency = sanitize_text_field($attributes['targetCurrency'] ?? 'COP');
$card_title      = sanitize_text_field($attributes['cardTitle'] ?? 'Tipo de Cambio Oficial');
$refresh_seconds = intval($attributes['refreshInterval'] ?? 3600);

// Generar una clave de caché única por combinación de parámetros
$transient_key = 'doneapi_rate_' . md5($base_currency . '_' . $target_currency);
$cached_data   = get_transient($transient_key);

if (false === $cached_data) {
    // La caché expiró o no existe: consultamos la API externa
    $api_url = add_query_arg([
        'base'   => $base_currency,
        'target' => $target_currency,
    ], 'https://api.doneapi.com/v1/finance/rates');

    $response = wp_remote_get($api_url, [
        'timeout' => 5,
        'headers' => [
            'Accept'     => 'application/json',
            'User-Agent' => 'DoneAPI-WordPress-Block/1.0',
        ],
    ]);

    if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
        $body = json_decode(wp_remote_retrieve_body($response), true);
        if ($body && isset($body['rate'])) {
            $cached_data = [
                'rate'      => floatval($body['rate']),
                'timestamp' => current_time('mysql'),
            ];
            // Almacenar en caché de transitorios por el tiempo configurado
            set_transient($transient_key, $cached_data, $refresh_seconds);
        }
    }
}

// Obtener las clases y estilos generados por los soportes de Gutenberg
$wrapper_attributes = get_block_wrapper_attributes([
    'class' => 'doneapi-rates-block-frontend',
]);
?>

<div <?php echo $wrapper_attributes; ?>>
    <div class="doneapi-rates-container">
        <header class="doneapi-rates-header">
            <h3 class="doneapi-rates-title"><?php echo esc_html($card_title); ?></h3>
            <span class="doneapi-rates-tag">Actualizado</span>
        </header>

        <?php if ($cached_data) : ?>
            <div class="doneapi-rates-body">
                <div class="doneapi-rate-main">
                    <span class="doneapi-currency-code"><?php echo esc_html($base_currency . ' / ' . $target_currency); ?></span>
                    <span class="doneapi-rate-number">$<?php echo number_format($cached_data['rate'], 2); ?></span>
                </div>
                <footer class="doneapi-rates-footer">
                    <small>Corte: <?php echo esc_html($cached_data['timestamp']); ?></small>
                </footer>
            </div>
        <?php else : ?>
            <div class="doneapi-rates-fallback">
                <p>Información de cotización temporalmente no disponible.</p>
            </div>
        <?php endif; ?>
    </div>
</div>

6. Registro del Bloque en el Plugin Principal

Para que WordPress cargue el bloque automáticamente con todas sus optimizaciones de rendimiento y scripts encolados, solo necesitamos una línea en el archivo principal del plugin:

<?php
/**
 * Plugin Name: DoneAPI Gutenberg Blocks
 * Description: Colección de bloques dinámicos conectados a microservicios y APIs en la nube.
 * Version: 1.0.0
 * Author: DoneAPI Engineering Team
 */

if (!defined('ABSPATH')) {
    exit;
}

add_action('init', function () {
    // Registra el bloque leyendo directamente el archivo block.json
    register_block_type(__DIR__ . '/build');
});

Al utilizar register_block_type apuntando al directorio donde reside block.json, WordPress:

  1. Registra automáticamente los scripts de React en el editor.
  2. Genera los estilos inline minificados para el frontend.
  3. Vincula el archivo render.php sin necesidad de ganchos manuales adicionales.
  4. Soporta soporte nativo de traducción con wp_set_script_translations.

7. Consultoría de Desarrollo en WordPress y APIs con DoneAPI

El desarrollo de bloques a medida para Gutenberg requiere dominar tanto el ecosistema moderno de JavaScript (React, JSX, Redux data stores de WordPress) como las mejores prácticas de backend en PHP (caché de transitorios, micro-servicios, seguridad y escalabilidad bajo alto tráfico).

En DoneAPI ayudamos a editoriales de noticias, plataformas de medios, agencias digitales y empresas de e-commerce a:

  • Desarrollo de Bloques Gutenberg Avanzados: Creación de librerías de bloques nativos en React adaptados al sistema de diseño corporativo de tu marca.
  • Integración con Microservicios en la Nube: Conexión de WordPress con APIs de facturación, ERPs (SAP, NetSuite), CRMs (HubSpot, Salesforce) y pasarelas de pago.
  • Optimización de Velocidad y Caché: Auditoría de rendimiento para sitios WordPress de alto tráfico, reduciendo los tiempos de carga y maximizando el Core Web Vitals (LCP, INP, CLS).
  • Plugins Especializados de Mercado: Accede a soluciones listas para producción como nuestro plugin de VikBooking + Mercado Pago ($7 USD) para reservas hoteleras automáticas.

💬 ¿Necesitas construir bloques de Gutenberg personalizados o conectar WordPress con APIs externas de forma escalable?
Chatea directamente con nuestros ingenieros senior por WhatsApp y te orientaremos en la arquitectura ideal para tu proyecto.

Desarrolla Bloques Gutenberg y Plugins Profesionales con DoneAPI

Aprovecha la potencia de React en WordPress sin comprometer la velocidad ni la estabilidad de tu servidor.

Hablar con un Desarrollador Senior de WordPress por WhatsApp

8. Conclusión

El editor de bloques Gutenberg no es una simple capa visual de maquetación: es un entorno de desarrollo de aplicaciones frontend en React perfectamente integrado con la potencia del backend de WordPress.

Dominar la arquitectura de bloques dinámicos con renderizado del lado del servidor y caché con la Transients API te permite enriquecer tus publicaciones con datos dinámicos de cualquier API REST en tiempo real, garantizando tiempos de carga ultrarrápidos y protegiendo tus servicios de sobrecargas accidentales.

Herramientas de Inteligencia Artificial para emprendedores

Desbloquea tu arsenal de automatización.

Regístrate gratis y accede a plantillas para n8n y Make.com, packs de prompts probados para IA, y guías exclusivas diseñadas para escalar tu negocio digital.

Crear cuenta y obtén recursos gratis