Cómo Eliminar Elementor de WordPress | Convertir 114 Páginas a Gutenberg en Un Día
Migrar un sitio de Elementor a Gutenberg es un desastre. No hay botón mágico. Tienes que reconstruir cada página a mano. Cuenta varias semanas. Empezar desde cero. Ese es el consenso, está en todas partes, lo leí unas diez veces antes de empezar realmente.
Un sitio de cliente. 114 piezas de contenido para migrar. Tema Hello Elementor (una cáscara vacía sin el plugin), licencias Pro vencidas, contenido encerrado dentro del JSON de Elementor que no se ve como nada si desactivas el constructor. Nadie quiere pagar por una migración técnica de varias semanas.
Yo tampoco. Pero no pensaste en serio que lo iba a hacer a mano, ¿verdad? 😏
RESUMEN: 114 páginas migradas en un día, cero reconstrucción manual, todas las URLs preservadas. Te mostraré exactamente cómo hacer lo mismo en cualquier proyecto de Elementor.

Por Qué Elementor Tiene Que Irse
Elementor era una buena opción hace unos años. El núcleo de WordPress era tosco. Gutenberg apenas era usable. Un constructor de arrastrar y soltar tenía sentido para gente que no quería tocar código. Eso era antes.
WordPress ha evolucionado. Gutenberg ahora es sólido. Full Site Editing cubre la mayoría de lo que los constructores de páginas solían vender. Los temas de bloques te permiten diseñar todo el sitio sin un solo plugin. Mientras tanto, Elementor se ha vuelto peso muerto: pesado, lento, de pago, y bloquea tu contenido en un formato propietario que nadie más puede leer.
El tema Hello Elementor es el peor culpable. Es una cáscara vacía, 100% dependiente del plugin. Apaga Elementor y tus páginas desaparecen. Simplemente desaparecen.
El problema real no es técnico. Es económico. Nadie quiere pagar por una migración técnica que toma varias semanas. El cliente necesita un sitio que funcione, no un proyecto de refactorización. Y cada guía que lees sobre dejar Elementor dice lo mismo. "No hay botón mágico." "Varias semanas de trabajo dedicado." "Esencialmente el sitio tiene que ser reconstruido."
Ese consenso mata las migraciones antes de que empiecen. Los propietarios se quedan atascados en un plugin que no les gusta, pagando licencias que no usan, en un sitio que no pueden editar sin abrir Elementor una vez más.
Para ser justos, Elementor sigue siendo decente para algunos casos. Una landing page de una sola vez. Un sitio temporal de eventos. Algo que el propietario no tocará en dos años. El problema no es el constructor en sí. Es el bloqueo a largo plazo del contenido que querrás conservar.
La Configuración: Una Contraseña de App, Un Agente
Esto es lo que todas las guías de migración te dicen que hagas. Montar un sitio de staging. Hacer un backup completo. Instalar el plugin "Elementor to Blocks" para conversión parcial. Reconstruir cada página a mano donde el plugin falla. Limpiar las clases residuales de Elementor. Hacer QA de todo. Subir a producción. Varias semanas, tiempo calendario.
Esto es lo que hice en su lugar. Abrí wp-admin en el sitio en vivo. Usuarios > Perfil > Contraseñas de Aplicación. Escribí un nombre, hice clic en Agregar. Copié la contraseña generada. Tiempo total transcurrido: 30 segundos.
Esa es toda la configuración. Sin flujo OAuth, sin webhook, sin plugin que instalar, sin entorno de staging. La contraseña de app es un token bearer con los mismos permisos que el usuario que la generó. Se la pasas a Claude Code junto con la URL del sitio, y el agente puede leer y escribir todo lo que la REST API expone. Posts, páginas, medios, usuarios, configuraciones.
Si quieres el tutorial completo de cómo funciona esto de principio a fin, escribí la configuración completa para conectar Claude Code a cualquier sitio WordPress hace unas semanas.
Una oración para lanzar: "Migra este sitio de Elementor a bloques nativos de Gutenberg, aquí está la URL y la contraseña de app, trabaja desde la REST API." Eso es todo. Sin ingeniería de prompts, sin instrucciones del sistema, sin lista de herramientas. Claude Code exploró la API por sí mismo, descifró el stack (versión de WordPress, tema activo, lista de plugins, tipos de post), extrajo un backup completo de cada post y página en un archivo JSON local, y empezó a escribir su primer script de conversión.
Cronología, números reales. Lanzado a las 8:15 de la mañana. Para el mediodía quedaban tres ajustes. Para la noche, terminado. 114 piezas de contenido en total (94 posts y 20 páginas). Todo el proceso técnico fue 100% dirigido por el agente. La única "intervención humana" fue yo transmitiendo los mensajes del cliente sobre qué quería conservar o eliminar.
Cómo Claude Code Convierte Elementor a Gutenberg
Primera decisión técnica, y es la que hizo que todo funcionara. Claude Code NO parseó _elementor_data. Ese es el JSON crudo que Elementor almacena en postmeta, profundamente anidado, la estructura varía entre versiones de Elementor, los tipos de widgets cambian nombres entre releases. Parsear eso habría sido un objetivo móvil.
En su lugar trabajó desde el HTML renderizado. La REST API expone content.rendered para cada post, que es el HTML final después de que Elementor haya hecho su trabajo. Más simple, más estable, portable entre versiones de Elementor. Si el HTML se ve bien en el frontend, el parser tiene algo que masticar.
El primer script que Claude Code escribió, convert-elementor.py. Aproximadamente lo que hace:
import json, re
from bs4 import BeautifulSoup
ELEMENTOR_WRAPPERS = {
"elementor-section", "elementor-container", "elementor-column",
"elementor-widget-wrap", "elementor-widget", "e-con", "e-con-inner",
"e-child", "elementor-row", "elementor-element"
}
def process_element(el):
"""Recorre el árbol, elimina wrappers de Elementor, emite bloques de Gutenberg."""
if el.name is None:
return str(el)
classes = set(el.get("class", []))
if classes & ELEMENTOR_WRAPPERS:
# Omite el wrapper, recurre en los hijos
return "".join(process_element(c) for c in el.children)
# Mapea widgets a bloques nativos de Gutenberg
if "elementor-widget-heading" in classes:
return convert_heading(el)
if "elementor-widget-image" in classes:
return convert_image(el)
if "elementor-widget-text-editor" in classes:
return convert_paragraph(el)
# ... otros widgets
return "".join(process_element(c) for c in el.children)
La función recursiva process_element es el corazón de todo. Recorre el árbol DOM, y cada vez que encuentra un wrapper de Elementor (alrededor de una docena de nombres de clase diferentes), omite el wrapper y sigue caminando hacia los hijos. Cuando encuentra un widget real, lo enruta a un convertidor dedicado. Los encabezados se vuelven wp:heading, las imágenes se vuelven wp:image, los editores de texto se vuelven wp:paragraph, y así sucesivamente. Las clases CSS y atributos data-* se eliminan con regex en el camino.
Dos ejemplos concretos.
Un encabezado de Elementor se ve así en el HTML renderizado:
<div class="elementor-widget elementor-widget-heading" data-id="a1b2c3">
<div class="elementor-widget-container">
<h2 class="elementor-heading-title elementor-size-default">
<span style="color: #333">Nuestro Enfoque</span>
</h2>
</div>
</div>
Después de la conversión:
<!-- wp:heading -->
<h2>Nuestro Enfoque</h2>
<!-- /wp:heading -->
El span inline con el estilo de color desapareció. También las clases de Elementor, los divs wrapper, los atributos data. Bloque limpio de Gutenberg, listo para renderizar en cualquier tema.
Una imagen de Elementor es similar. El constructor envuelve el <img> en una figura que vive dentro de dos o tres divs con clases como elementor-widget-image. Gutenberg espera una figura con la clase wp-block-image y el comentario de bloque correcto alrededor:
<!-- wp:image -->
<figure class="wp-block-image">
<img src="..." alt="..."/>
</figure>
<!-- /wp:image -->
Mismo patrón. Eliminar los wrappers, reconstruir el markup limpio, envolver en un comentario de bloque de Gutenberg.
Última pieza, empujar el contenido convertido de vuelta a WordPress. Aquí es donde las cosas se pusieron raras. El sitio del cliente está detrás de Cloudflare, y Cloudflare bloqueó cada librería HTTP de Python que probé (requests, urllib, httpx). El user-agent se veía lo suficientemente sospechoso como para obtener 403 en cada POST. Claude Code se dio cuenta de esto por sí mismo, después de algunas llamadas fallidas, y cambió a curl en un subproceso. El cuerpo JSON va a un archivo temporal, curl lo lee con @filename, Cloudflare ve un user-agent normal de curl y lo deja pasar.
No es limpio. Es un hack. Pero funcionó para toda la ejecución, y en una línea de tiempo lo suficientemente larga, cada "solución temporal" se convierte en infraestructura crítica de todos modos.
Esa es la advertencia que te debo: la solución de curl está atada a este host específico. En un sitio sin Cloudflare, el cliente HTTP normal de Python habría funcionado bien. Tu experiencia puede variar dependiendo de la configuración del CDN y el host.
Qué Se Rompió (y Cómo Se Arregló Solo)
Después del primer pase el sitio estaba arriba. Cada página cargaba. Sin 500s, sin pantallas blancas. Pero el contenido estaba plagado de problemas, tres sabores diferentes de roto, y aparecieron aproximadamente en ese orden.
El primero fueron espaciadores fantasma por todas partes. Elementor usa párrafos vacíos con margen CSS como separadores visuales, y cuando parseas el HTML ingenuamente, esos párrafos vacíos se convierten en bloques wp:spacer. Multiplica eso por 114 páginas y obtienes docenas de espaciadores apilados uno encima del otro, creando huecos verticales absurdos en el contenido.
Secciones flotando solas en el medio de la página. Footers a mitad del viewport. Todo el layout haciendo su mejor imitación de un reset CSS que salió mal. Claude Code se dio cuenta por sí mismo después de que señalé una página y dije "esto se ve mal". Escribió un segundo script, fix-spacers.py, que volvió a extraer cada post, eliminó los bloques espaciadores con regex, y los reemplazó con saltos de línea regulares donde era apropiado. 47 piezas de contenido limpiadas en un lote.
Luego vinieron los errores de bloque inválido. Gutenberg valida el markup de bloques estrictamente. Si un bloque wp:image tiene una figura sin la clase wp-block-image, Gutenberg arroja "Este bloque contiene contenido inesperado o inválido." Lo mismo para un wp:heading que todavía tiene spans de Elementor adentro. El bloque carga pero el editor se niega a modificarlo, lo cual es peor que si estuviera simplemente roto visualmente.
El cliente quería editar sus propias páginas, ese era todo el punto. Tercer script, fix-blocks.py. Re-parsear cada bloque, reconstruir el HTML interno desde cero usando el formato que Gutenberg espera, empujarlo de vuelta. 81 piezas de contenido arregladas, divididas en 4 lotes paralelos para acelerar las cosas. Claude Code decidió el paralelismo por sí mismo. No se lo pedí.
Aquí está el punto narrativo que me importa. Los primeros dos problemas, Claude Code los resolvió completamente por sí mismo. Tres scripts en total, cada uno escrito para arreglar los problemas que el anterior había creado. Bucle de retroalimentación autónomo, misma sesión, sin reinicio. Yo señalé síntomas. El agente escribió el diagnóstico, escribió la solución, ejecutó la solución, verificó la solución.
El tercer sabor de roto es el límite real. Elementor Pro incluye widgets que no tienen equivalente en Gutenberg nativo. El slider de la homepage, desapareció (no hay bloque slider en core). El popup de Mailchimp, desapareció. El formulario de contacto de Elementor Pro, desapareció. El widget de iconos sociales, texto preservado pero los iconos visuales eliminados. Estos no son bugs de conversión. Son widgets propietarios que no existen fuera de Elementor Pro, y ningún parser en el mundo los va a inventar.
El código lo puede arreglar. El vendor lock-in no.
El Último 5% Lo Harás Tú Mismo
Anécdota corta primero. En un punto Claude Code necesitaba instalar el tema Twenty Twenty-Five como respaldo. La REST API de WordPress no expone la instalación de temas (por buenas razones, es una superficie de seguridad). Así que el agente abrió un navegador vía su herramienta MCP, se logueó en wp-admin, navegó a Apariencia > Temas > Agregar Nuevo, buscó, hizo clic en Instalar, hizo clic en Activar. Lo hizo por sí mismo. Lo vi pasar en los logs.
Menciono esto porque el marco de "95% automatizado" necesita una advertencia. El agente manejó casi todo lo que la REST API permitía, y cuando la API no permitía algo, encontró otro canal. Lo que queda para que el humano haga no es trabajo técnico que el agente no puede manejar. Son decisiones de negocio que el agente no debería estar tomando solo.
Concretamente, lo que tuve que tocar manualmente al final:
- Qué plugin de formularios instalar para reemplazar los formularios de Elementor Pro. WPForms Lite, Contact Form 7, o Fluent Forms, cada uno tiene compromisos. No es una decisión del agente.
- Cómo reintegrar el registro de newsletter. Embed nativo de Mailchimp, plugin MC4WP, o un flujo de registro completamente nuevo.
- Elegir una nueva imagen hero porque la anterior tenía texto integrado.
- Revisar los meta de Rank Math SEO en las páginas de aterrizaje clave para asegurar que nada regresara.
- Limpiar las filas
_elementor_*sobrantes enpostmeta(WP-CLI o SQL directo, para aligerar la base de datos). - Remover los archivos del tema Hello Elementor (tampoco hay endpoint REST para eso).
95% automatizado, 5% manual. Y ese 5% es trabajo de decisión, no trabajo de reconstrucción. Antes de apuntar un agente de código a un sitio de producción, quieres definir exactamente qué se le permite tocar y qué tiene que escalar. Esa es toda la razón por la que construí la disciplina de alcance de ejecución que uso antes de lanzar un agente en producción. Alcance claro, reglas de escalación claras, condiciones de parada claras. Sin eso estás apostando en un sitio en vivo y esperando que el agente se autocorrija antes de que rompa algo.
La REST API en sí tiene límites duros que vale la pena conocer. Sin eliminación de temas, sin acceso al código de plugins, sin ejecución PHP, sin sistema de archivos. Para cualquier cosa fuera de la superficie de la API necesitas SSH o wp-admin. Define el perímetro antes de lanzar.
El Botón Que No Existía
114 piezas de contenido. Tres scripts. Un día. Errores tipográficos arreglados en el camino, páginas clave revisadas, todas las URLs preservadas. El cliente ahora edita sus propias páginas sin llamarme.
Todas las guías tienen razón. No hay botón mágico para Elementor a Gutenberg. Pero un agente que escribe sus propios scripts de conversión y arregla sus propios bugs en una sesión continua, eso no es un botón.
Es mejor que un botón 🚀
Fuentes
- WordPress REST API Handbook
- Guías de consenso sobre migración de Elementor a Gutenberg: Blog Marketing Academy, Crocoblock, Ulement ("no hay botón mágico", "varias semanas de trabajo dedicado")
(*) La portada es generada por IA. Resulta que el único botón mágico real en toda esta historia fue el que hizo la imagen del encabezado.