jQuery resolvió problemas reales de compatibilidad entre navegadores durante más de una década. En 2008, cuando este artículo fue publicado originalmente, escribir JavaScript cross-browser sin jQuery era genuinamente doloroso. Internet Explorer 6 manejaba los eventos de forma diferente, los selectores CSS eran inconsistentes y AJAX requería implementaciónes específicas de XMLHttpRequest para cada navegador.
Descubre más sobre optimización de velocidad WordPress en WPPoland.
En 2026, cada problema que jQuery resolvió ahora se maneja de forma nativa por los navegadores. La pregunta ya no es si migrar, sino cómo hacerlo de forma segura sin romper la funcionalidad existente.
Por qué jQuery es deuda técnica en 2026
jQuery 3.7 pesa 87KB sin comprimir (30KB gzipped). Eso puede sonar pequeño, pero considera lo que cuesta:
- Total Blocking Time (TBT): jQuery debe parsearse y ejecutarse antes de que cualquier código dependiente se ejecute. En dispositivos móviles de gama media, esto añade 150-300ms al TBT.
- Interaction to Next Paint (INP): El sistema de delegación de eventos de jQuery añade sobrecarga a cada interacción del usuario, empeorando mediblemente las puntuaciones INP.
- Cadena de dependencias: Cargar jQuery significa que cada script que depende de él debe esperar, creando una cascada de recursos bloqueantes.
- Código redundante: Cada método jQuery que llamás tiene un equivalente nativo que el navegador ya incluye. Estás pagando dos veces por la misma funcionalidad.
Benchmarks de rendimiento: jQuery vs vanilla JS
Mediciones del mundo real en un tema WordPress con interacciones típicas (toggle de menú, cambio de pestañas, validación de formulario, carga AJAX de más contenido):
| Métrica | Con jQuery | Sin jQuery | Mejora |
|---|---|---|---|
| Tamaño total JS | 142KB | 55KB | -61% |
| TBT (móvil) | 480ms | 180ms | -62% |
| INP (p75) | 220ms | 95ms | -57% |
| LCP | 2.1s | 1.7s | -19% |
| Rendimiento Lighthouse | 72 | 94 | +22 puntos |
Estos números provienen de un sitio WordPress en producción con GeneratePress y WooCommerce, probado en un Moto G Power (un dispositivo de gama media representativo).
JavaScript moderno (ES2024+) reemplaza cada patrón de jQuery
La especificación ES2024, completamente soportada en Chrome 124+, Firefox 126+, Safari 17.4+ y Edge 124+, proporciona alternativas nativas para cada patrón común de jQuery.
Selección DOM
// jQuery
const $buttons = $('.btn');
const $container = $('#main-container');
const $firstItem = $('.menu-item:first');
// Vanilla JS (ES2024+)
const buttons = document.querySelectorAll('.btn');
const container = document.getElementById('main-container');
const firstItem = document.querySelector('.menu-item');
// Selección con alcance (como jQuery .find())
const navLinks = container.querySelectorAll('a.nav-link');
Diferencia clave: querySelectorAll devuelve un NodeList estático, no una colección viva. Esto es realmente más seguro porque la lista no cambia inesperadamente cuando el DOM muta.
Manejo de eventos
// jQuery
$('.btn').click(function () {
$(this).toggleClass('active');
});
$('.menu').on('click', '.menu-item', function () {
// evento delegado
});
// Vanilla JS
document.querySelectorAll('.btn').forEach(btn => {
btn.addEventListener('click', () => {
btn.classList.toggle('active');
});
});
// Delegación de eventos (reemplaza .on() con selector)
document.querySelector('.menu').addEventListener('click', (e) => {
const item = e.target.closest('.menu-item');
if (item) {
// manejar clic en elemento del menú
}
});
El método closest() es el equivalente moderno de la coincidencia de eventos delegados de jQuery. Recorre el árbol DOM hacia arriba para encontrar el ancestro más cercano que coincida con un selector.
Manipulación de clases
// jQuery
$el.addClass('active');
$el.removeClass('hidden');
$el.toggleClass('open');
$el.hasClass('visible');
// Vanilla JS
el.classList.add('active');
el.classList.remove('hidden');
el.classList.toggle('open');
el.classList.contains('visible');
// Múltiples clases a la vez
el.classList.add('active', 'highlighted', 'animate-in');
el.classList.remove('hidden', 'collapsed');
AJAX con fetch API y async/await
// jQuery
$.ajax({
url: '/wp-json/wp/v2/posts',
method: 'GET',
data: { per_page: 5 },
success: function (posts) { renderPosts(posts); },
error: function (xhr) { console.error(xhr); }
});
// Vanilla JS (async/await moderno)
async function loadPosts() {
try {
const response = await fetch('/wp-json/wp/v2/posts?per_page=5');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const posts = await response.json();
renderPosts(posts);
} catch (error) {
console.error('Error al cargar posts:', error);
}
}
// POST con nonce (patrón WordPress)
async function submitForm(data) {
const response = await fetch('/wp-json/custom/v1/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': wpApiSettings.nonce,
},
body: JSON.stringify(data),
});
return response.json();
}
Animaciones sin jQuery
Las funciones .fadeIn(), .slideDown() y .animate() de jQuery pueden reemplazarse con transiciones CSS, animaciones CSS o la Web Animations API.
// jQuery
$('.panel').slideDown(300);
$('.modal').fadeIn(200);
// Enfoque CSS (preferido por rendimiento)
// En CSS:
// .panel { max-height: 0; overflow: hidden; transition: max-height 0.3s ease; }
// .panel.open { max-height: 500px; }
// En JS:
panel.classList.add('open');
// Web Animations API (para animaciones complejas y programáticas)
modal.animate(
[
{ opacity: 0, transform: 'scale(0.95)' },
{ opacity: 1, transform: 'scale(1)' },
],
{ duration: 200, easing: 'ease-out', fill: 'forwards' }
);
La Web Animations API se ejecuta en el hilo del compositor, lo que significa que las animaciones no bloquean el hilo principal. Las animaciones de jQuery se ejecutan en el hilo principal y causan jank en dispositivos más lentos.
Manipulación DOM
// jQuery
$('<div class="notice">Hola</div>').appendTo('#container');
$('.old-element').replaceWith('<span>Nuevo</span>');
$('.item').remove();
$('.list').empty();
// Vanilla JS
const notice = document.createElement('div');
notice.className = 'notice';
notice.textContent = 'Hola';
container.append(notice);
// O usa insertAdjacentHTML para cadenas HTML
container.insertAdjacentHTML('beforeend', '<div class="notice">Hola</div>');
// Reemplazar
oldElement.replaceWith(Object.assign(document.createElement('span'), { textContent: 'Nuevo' }));
// Eliminar
item.remove();
// Vaciar
list.replaceChildren();
Document ready
// jQuery
$(document).ready(function () { /* ... */ });
$(function () { /* atajo */ });
// Vanilla JS
document.addEventListener('DOMContentLoaded', () => {
// DOM listo
});
// O simplemente coloca tu <script> con type="module" al final del <body>
// Los módulos se difieren por defecto, así que el DOM ya está listo
Web Components: el reemplazo moderno de los plugins jQuery
Los plugins jQuery proporcionaban componentes UI reutilizables (sliders, modales, pestañas, acordeones). En 2026, los Web Components ofrecen una alternativa basada en estándares con mejor encapsulación.
Ejemplo: un componente de panel desplegable
class TogglePanel extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
const title = this.getAttribute('title') || 'Alternar';
this.shadowRoot.innerHTML = `
<style>
:host { display: block; margin: 1rem 0; }
button {
width: 100%; padding: 0.75rem 1rem;
background: #f5f5f5; border: 1px solid #ddd;
cursor: pointer; text-align: left;
font-size: 1rem; font-weight: 600;
}
.content {
display: none; padding: 1rem;
border: 1px solid #ddd; border-top: none;
}
:host([open]) .content { display: block; }
</style>
<button part="trigger">${title}</button>
<div class="content"><slot></slot></div>
`;
this.shadowRoot.querySelector('button').addEventListener('click', () => {
this.toggleAttribute('open');
});
}
}
customElements.define('toggle-panel', TogglePanel);
Uso en HTML:
<toggle-panel title="Información de envío">
<p>Envío gratuito en pedidos superiores a 50 EUR.</p>
</toggle-panel>
Los Web Components proporcionan encapsulación Shadow DOM (los estilos no se filtran), slots para proyección de contenido y callbacks de ciclo de vida. Funcionan en todos los navegadores modernos sin polyfills.
Cuándo usar Web Components vs un framework
| Escenario | Recomendación |
|---|---|
| Widget interactivo simple (acordeón, pestañas, modal) | Web Component |
| SPA completa (aplicación de una sola página) | React / Vue / Svelte |
| Bloque WordPress (Gutenberg) | React (estándar WordPress) |
| Componente compartido entre múltiples sitios | Web Component |
| Gestión de estado compleja | Framework con librería de estado |
Estrategia de migración para proyectos WordPress
Paso 1: Auditar tu uso de jQuery
Ejecuta este comando en el directorio de tu tema para encontrar todas las referencias a jQuery:
grep -rn '\$(\|jQuery\.\|jQuery(' --include='*.js' --include='*.php' .
Categoriza cada uso:
- Tu código (tema/plugin personalizado): migra esto
- Plugin de terceros: déjalo, el plugin gestiona sus propias dependencias
- Admin de WordPress: no tocar, el núcleo de WordPress maneja esto
Paso 2: Crear un plan de migración
Prioriza por impacto:
- Código frontend del tema (afecta a cada visitante) - migra primero
- Frontend de plugin personalizado - migra segundo
- Personalizaciones del lado admin - migra último (menor tráfico)
Paso 3: Reemplazar patrones incrementalmente
No reescribas todo de una vez. Reemplaza un archivo a la vez:
- Elimina
array('jquery')del array de dependencias dewp_enqueue_scriptdel archivo - Reemplaza todos los patrones jQuery con equivalentes vanilla JS
- Prueba en Chrome, Firefox, Safari y Edge
- Prueba con todos los plugins activos habilitados
- Ejecuta Lighthouse antes y después para medir la mejora
Paso 4: Manejar el patrón AJAX de WordPress
El AJAX legacy de WordPress usa admin-ajax.php con jQuery:
// Patrón antiguo (jQuery + admin-ajax)
jQuery.post(ajaxurl, {
action: 'my_custom_action',
nonce: myData.nonce,
post_id: 123,
}, function (response) {
console.log(response);
});
// Patrón moderno (fetch + REST API)
async function myCustomAction(postId) {
const response = await fetch('/wp-json/myplugin/v1/action', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': myData.nonce,
},
body: JSON.stringify({ post_id: postId }),
});
return response.json();
}
El enfoque de la REST API es más rápido (sin sobrecarga de admin-ajax.php), más cacheable y sigue los estándares modernos de desarrollo WordPress.
Paso 5: Registrar scripts correctamente
// Antes (con dependencia jQuery)
wp_enqueue_script(
'my-theme-scripts',
get_template_directory_uri() . '/js/main.js',
array('jquery'),
'1.0.0',
true
);
// Después (sin jQuery, con soporte de módulos)
wp_enqueue_script_module(
'my-theme-scripts',
get_template_directory_uri() . '/js/main.js',
array(),
'2.0.0'
);
WordPress 6.5+ soporta wp_enqueue_script_module() que carga scripts como módulos ES con type="module", habilitando la sintaxis nativa de import/export.
Cuándo mantener jQuery todavía tiene sentido
jQuery puede seguir estando justificado si:
- Base de código legacy con más de 50 dependencias de plugins jQuery: El costo de migración supera el beneficio de rendimiento. Planifica una eliminación gradual durante 6-12 meses.
- Personalizaciones del admin de WordPress: El área de administración ya carga jQuery. Añadir tus propios scripts de admin con dependencia jQuery no cuesta nada extra.
- Requisitos de plugins de terceros: Algunos plugins populares (ciertos constructores de formularios, constructores de páginas) requieren jQuery. No luches contra la dependencia si no puedes controlarla.
- Brecha de habilidades del equipo: Si tu equipo de desarrollo no está cómodo con JS moderno, invierte en formación antes de forzar una migración.
El objetivo es la mejora pragmática, no la pureza ideológica. Elimina jQuery donde te cuesta rendimiento y no añade valor. Mantenlo donde eliminarlo rompería cosas o costaría más de lo que ahorra.
Características ES2024+ que reemplazan utilidades comunes de jQuery
Clon estructurado (copia profunda)
// jQuery
const copy = $.extend(true, {}, original);
// ES2024+
const copy = structuredClone(original);
Iteración tipo array
// jQuery
$.each(items, function (index, item) { /* ... */ });
// ES2024+
items.forEach((item, index) => { /* ... */ });
// O con Array.from para NodeLists
Array.from(document.querySelectorAll('.item')).map(item => item.textContent);
// O operador spread
[...document.querySelectorAll('.item')].filter(item => item.dataset.active);
Patrones Deferred/Promise
// jQuery
const deferred = $.Deferred();
deferred.resolve('hecho');
deferred.promise().then(val => console.log(val));
// ES2024+
const promise = new Promise((resolve) => resolve('hecho'));
promise.then(val => console.log(val));
// Promise.withResolvers() - característica ES2024
const { promise, resolve, reject } = Promise.withResolvers();
IntersectionObserver (reemplaza manejadores de scroll de jQuery)
// jQuery (manejador de scroll costoso)
$(window).scroll(function () {
$('.lazy-image').each(function () {
if ($(this).offset().top < $(window).scrollTop() + $(window).height()) {
$(this).attr('src', $(this).data('src'));
}
});
});
// Vanilla JS (eficiente, fuera del hilo principal)
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
document.querySelectorAll('.lazy-image').forEach(img => observer.observe(img));
Alternativas de librerías de utilidades (si necesitas un helper)
Si te encuentras escribiendo los mismos patrones vanilla JS repetidamente, considera una micro-librería en lugar de jQuery:
| Librería | Tamaño | Propósito |
|---|---|---|
| Alpine.js | 15KB | Reactividad declarativa (x-data, x-on) |
| htmx | 14KB | AJAX, WebSocket, SSE vía atributos HTML |
| Petite-Vue | 6KB | Sintaxis de plantilla compatible con Vue |
| Ninguna (vanilla) | 0KB | Mejor rendimiento, control total |
Para temas WordPress en 2026, la recomendación es vanilla JS para interacciones simples y Alpine.js o htmx si necesitas comportamiento declarativo sin un framework completo.
Mejores prácticas de jQuery en 2026
Si todavía mantienes código jQuery - ya sea por elección o porque una base de código legacy lo exige - estas prácticas minimizan el daño:
- Carga jQuery del core de WordPress, nunca desde un CDN. WordPress incluye una versión compatible. Cargar una segunda copia desde
cdnjsocode.jquery.comsignifica el doble de peso y posibles conflictos de versión. - Usa
jQueryen lugar de$en WordPress. WordPress ejecuta jQuery en modo no-conflicto. Envolver el código enjQuery(function($) { ... })previene colisiones con otras librerías. - Evita anidar
.ready(). Un solojQuery(function($) { ... })es suficiente. Las llamadas anidadas a.ready()crean pirámides de callbacks y confunden el orden de ejecución. - Cachea los selectores. Cada llamada a
$('.my-class')recorre el DOM. Almacena los resultados en una variable cuando uses el mismo selector más de una vez. - Delega eventos en lugar de vincularlos a elementos individuales. Usa
$(parent).on('click', '.child', handler)en lugar de$('.child').click(handler). Esto maneja elementos añadidos dinámicamente y usa menos listeners de eventos. - No uses jQuery para animaciones CSS. Usa transiciones CSS o la Web Animations API. El método
.animate()de jQuery se ejecuta en el hilo principal y causa jank. - Declara
jquerycomo dependencia solo cuando sea necesario. Si un archivo de script no usa jQuery, elimínalo del array de dependencias dewp_enqueue_scriptpara evitar cargar jQuery innecesariamente.
Última versión de jQuery y qué cambió
En 2026, jQuery 3.7.1 es la última versión estable (publicada en agosto de 2023). jQuery 4.0.0-beta.2 ha estado en beta desde febrero de 2024, sin fecha de lanzamiento estable anunciada.
Cambios de jQuery 4.0 que importan para WordPress:
| Cambio | Impacto |
|---|---|
| Elimina soporte para IE 11 | Sin efecto - WordPress 6.6+ ya eliminó IE 11 |
Elimina APIs obsoletas (.click(), .bind(), .delegate()) | Los plugins que las usen se romperán |
| Bundle más pequeño (~68KB vs 87KB) | Mejora modesta, aún más pesado que sin jQuery |
$.ajax() basado en FormData para subida de archivos | API más limpia, pero fetch() lo hace nativamente |
El core de WordPress actualmente incluye jQuery 3.7.1 y no se ha comprometido a incluir la 4.0. La conclusión práctica: no esperes a jQuery 4 para mejorar el rendimiento. Migra a vanilla JS donde sea posible, y usa las mejores prácticas de jQuery 3.7.1 donde la migración aún no sea viable.
Quién sigue usando jQuery en 2026
jQuery sigue cargándose en un estimado del 77% de todos los sitios web (según W3Techs), en gran parte porque WordPress, Shopify y sitios empresariales legacy lo incluyen por defecto. Pero “cargado” no significa “necesario.”
El desglose:
- Sitios WordPress: jQuery se carga en prácticamente cada página WordPress porque la barra de admin y muchos plugins populares dependen de él. En el frontend, la dependencia real suele ser solo uno o dos scripts.
- Temás de Shopify: La mayoría de los temas de Shopify incluyen jQuery para interacciones del carrito. Shopify no se ha movido para deprecarlo.
- Sistemas empresariales legacy: Bancos, portales gubernamentales y grandes plataformas de e-commerce a menudo tienen jQuery incrustado en bases de código que datan de 2010-2015. La migración es costosa y de baja prioridad.
- Proyectos nuevos: Casí ninguno. React, Vue, Svelte y vanilla JS dominan el desarrollo de nuevos proyectos. Ningún framework moderno o plantilla de inicio incluye jQuery.
Si estás comenzando un nuevo tema o plugin de WordPress en 2026, no hay razón para añadir jQuery como dependencia. Las APIs del navegador son suficientes para cada patrón común. Para más información sobre cómo construir plugins correctamente, consulta la sección de mejores prácticas para plugins WordPress en nuestra guía de stack de plugins.
Conclusión: la lista de verificación de migración
- Audita todo el uso de jQuery en tu tema y plugins personalizados
- Mide los Core Web Vitals actuales como referencia
- Reemplaza el código frontend del tema primero (mayor impacto en visitantes)
- Usa
querySelector,addEventListener,fetch,classListy Web Animations API - Considera Web Components para elementos UI reutilizables
- Usa
wp_enqueue_script_module()para soporte de módulos ES - Prueba en todos los navegadores principales después de cada migración de archivo
- Mide los Core Web Vitals de nuevo y documenta la mejora
- Mantén jQuery solo para scripts de admin y dependencias de plugins de terceros
- Forma a tu equipo en patrones modernos de JavaScript
La plataforma web en 2026 proporciona todo lo que jQuery ofrecía, y más. Cada kilobyte de JavaScript innecesario que eliminas hace tu sitio WordPress más rápido, más accesible y más fácil de mantener.

