Saltar al contenido principal
Guía práctica

Eliminar JavaScript No Usado: Guía Técnica SEO 2026

Por qué el JavaScript no utilizado daña tu Core Web Vitals

Abre Chrome DevTools en cualquier sitio web de tamaño medio, activa la pestaña Coverage y recarga la página. Lo que verás en la mayoría de los casos es que entre el 40% y el 70% del JavaScript descargado aparece marcado en rojo: código que no se ejecutó en esa página. La página web media envía 500 KB de JavaScript, según HTTP Archive, y buena parte de ese peso no sirve para nada en la carga actual.

El CSS bloquea el renderizado pero se procesa rápidamente. El JavaScript, en cambio, se descarga, se parsea, se compila y se ejecuta, con un coste computacional significativamente mayor. Según el equipo de V8 de Google, cada 100 KB de JavaScript adicional añade entre 150 y 350 ms de procesamiento en un dispositivo móvil de gama media (un Samsung A52 o un Xiaomi Redmi Note, los dispositivos más vendidos en España). En dispositivos de gama alta como un iPhone 15 Pro, el mismo JavaScript tarda 50-100 ms. La diferencia de 4x a 7x entre gama alta y gama media explica por qué un sitio web puede parecer rápido en el MacBook del desarrollador y ser dolorosamente lento para el 60% de sus usuarios reales.

El impacto en Core Web Vitals es directo y medible. El LCP se retrasa cuando JavaScript bloquea el hilo principal durante la carga inicial, impidiendo que el navegador pinte el contenido más grande. El INP (Interaction to Next Paint) se degrada cuando scripts de larga ejecución mantienen el hilo principal ocupado, impidiendo que el navegador responda a las interacciones del usuario. El CLS aumenta cuando JavaScript modifica el DOM después del renderizado inicial, desplazando elementos visibles.

Chrome DevTools Coverage revela la magnitud del desperdicio. En las auditorías que realizamos, entre el 40% y el 70% del JavaScript descargado no se ejecuta en la página actual. Ese código muerto consume ancho de banda, tiempo de parseo y CPU sin aportar ninguna funcionalidad. Es el equivalente digital de pagar por un almacén lleno de mercancía que nunca se vende: un coste fijo que lastra los márgenes sin generar valor.

La relación con la velocidad web y SEO es clara. Google mide los Core Web Vitals con datos de campo —usuarios reales de Chrome— y usa esas métricas como señal de ranking. Un sitio con 500 KB de JavaScript innecesario puede tener un INP de 400 ms en dispositivos de gama media, muy por encima del umbral de 200 ms que Google considera “bueno”. Eliminar ese JavaScript no utilizado puede ser la diferencia entre superar o no los umbrales de Core Web Vitals que impactan directamente en el posicionamiento orgánico.

Cómo identificar JavaScript no utilizado con Chrome DevTools

La identificación precisa de JavaScript no utilizado es el paso previo a cualquier optimización. Sin un diagnóstico claro de qué código no se ejecuta, las intervenciones son especulativas y el riesgo de romper funcionalidad es real.

Chrome DevTools Coverage es la herramienta principal. Se accede desde DevTools (F12) > Ctrl+Shift+P > “Show Coverage” > botón de recarga. Coverage analiza la ejecución de cada archivo JavaScript y CSS durante una sesión de usuario, marcando en rojo las líneas que no se ejecutaron y en azul las que sí. El resultado es un porcentaje de utilización por archivo: un archivo con 30% de utilización significa que el 70% de su código es potencialmente eliminable.

La metodología correcta para usar Coverage es: primero, hacer una recarga limpia de la página para capturar el CSS y JS de la carga inicial; segundo, interactuar con todos los elementos de la página —menús, modales, tabs, formularios— para activar código que se ejecuta bajo demanda; tercero, navegar a 3-5 páginas representativas del sitio. El resultado final muestra la utilización real del JavaScript a nivel de sitio, no solo de una página individual. Un archivo con un 15% de utilización tras esta metodología completa es un candidato claro para optimización.

Lighthouse complementa Coverage con la auditoría “Remove unused JavaScript”. Mientras Coverage muestra datos granulares por archivo y línea, Lighthouse cuantifica el ahorro potencial en KB y segundos. La auditoría lista los archivos con mayor cantidad de código no utilizado y estima el impacto en el tiempo de carga. Para priorización, los archivos que Lighthouse marca con más de 50 KB de ahorro potencial son los que merecen atención inmediata.

Webpack Bundle Analyzer (o su equivalente en Vite: rollup-plugin-visualizer) proporciona una vista de árbol del bundle JavaScript completo, mostrando qué dependencias npm ocupan más espacio. Es común descubrir que una sola dependencia —moment.js (300 KB), lodash completo (70 KB), o una librería de iconos que se importa entera— representa el 30-50% del bundle total. Estas dependencias son los candidatos prioritarios para tree shaking, reemplazo o eliminación.

Un patrón que detectamos frecuentemente en e-commerces españoles es la acumulación de scripts de tracking. Google Tag Manager, Facebook Pixel, Hotjar, Clarity, TikTok Pixel, Pinterest Tag, Google Ads remarketing, y scripts de plataformas de afiliación pueden sumar fácilmente 300-500 KB de JavaScript adicional. Cada uno de estos scripts se ejecuta en el hilo principal, compitiendo con el JavaScript de la aplicación por tiempo de CPU. La auditoría de scripts de terceros es tan importante como la del código propio.

JavaScript de terceros: el peor culpable (y cómo gestionarlo)

El JavaScript de terceros es la fuente más frecuente de JavaScript no utilizado y, paradójicamente, la más difícil de controlar. A diferencia del código propio, donde el equipo de desarrollo puede modificar, optimizar o eliminar, el código de terceros es una caja negra que se carga desde servidores externos y ejecuta lógica sobre la que no hay control directo.

Según Web Almanac de HTTP Archive, el sitio web mediano carga scripts de 8 orígenes de terceros diferentes. Cada origen introduce latencia de DNS, establecimiento de conexión TCP/TLS y transferencia de datos. Pero el coste real no es la descarga: es la ejecución. Un script de chat en vivo que pesa 150 KB tras descarga puede expandirse a 400 KB de JavaScript ejecutable, monopolizando el hilo principal durante 500-800 ms en dispositivos de gama media.

Las estrategias para gestionar JavaScript de terceros, ordenadas por impacto, son las siguientes.

Auditar y eliminar lo innecesario. El primer paso es inventariar todos los scripts de terceros cargados en el sitio y cuestionar si cada uno de ellos aporta valor proporcional a su coste de rendimiento. Un widget de encuestas que generó 12 respuestas en los últimos 6 meses no justifica 80 KB de JavaScript en cada carga de página. Un script de A/B testing que no se usa activamente no justifica 120 KB de SDK. La eliminación pura es la optimización más eficaz: zero bytes es siempre más rápido que cualquier cantidad optimizada.

Facade Pattern para carga bajo demanda. El Facade Pattern consiste en mostrar un placeholder visual ligero (una imagen estática o un componente CSS puro) en lugar del widget real, y cargar el JavaScript completo solo cuando el usuario interactúa con él. El ejemplo más documentado es el embed de YouTube: reemplazar el iframe de YouTube por una imagen del thumbnail con un botón de play, y cargar el player completo solo al hacer clic, ahorra 500-700 KB de JavaScript en la carga inicial. El mismo patrón aplica a chatbots, mapas de Google, y widgets de redes sociales.

Defer y async para scripts no críticos. El atributo defer en un <script> indica al navegador que descargue el script en paralelo con el parseo del HTML pero que lo ejecute solo después de que el DOM esté completo. El atributo async descarga en paralelo pero ejecuta inmediatamente cuando la descarga termina, potencialmente interrumpiendo el parseo. Para scripts de analytics y tracking, defer es la elección correcta. Para scripts que deben ejecutarse lo antes posible sin depender del DOM (como precargas de contenido), async es apropiado.

Google Tag Manager con reglas de activación. GTM permite controlar qué scripts se cargan en qué páginas mediante reglas de activación (triggers). Un pixel de Facebook configurado para activarse solo en páginas de producto y checkout, en lugar de en todas las páginas, reduce la carga de JavaScript en las páginas informativas donde ese pixel no genera conversiones. Esta granularidad requiere configuración inicial, pero el beneficio de rendimiento es proporcional al número de scripts gestionados.

Tree shaking y code splitting: eliminar código muerto en el build

Tree shaking y code splitting son técnicas complementarias que operan en el pipeline de build para reducir el JavaScript que llega al navegador. Tree shaking elimina código que existe pero nunca se ejecuta. Code splitting divide el código en fragmentos que se cargan bajo demanda.

Tree shaking es un proceso automático de los bundlers modernos (Webpack 5, Vite/Rollup, esbuild) que analiza los imports de módulos ES (import/export) e identifica las exportaciones que ningún otro módulo consume. Esas exportaciones muertas se eliminan del bundle final. El nombre “tree shaking” viene de la metáfora de sacudir un árbol para que caigan las hojas muertas.

La condición fundamental para que tree shaking funcione es usar módulos ES (ESM) con import y export, no CommonJS con require y module.exports. CommonJS es dinámico —los imports pueden depender de condiciones en tiempo de ejecución— y los bundlers no pueden determinar estáticamente qué se usa. ESM es estático: los imports se declaran en la raíz del archivo y el bundler puede analizarlos sin ejecutar el código.

Un caso paradigmático es la diferencia entre import _ from 'lodash' (importa la librería completa, 70 KB) e import { debounce } from 'lodash-es' (importa solo la función necesaria, 3 KB). El uso de la variante ESM (lodash-es) permite al bundler aplicar tree shaking y eliminar las 300+ funciones de lodash que no se usan. Para dependencias que no ofrecen variante ESM, alternativas como babel-plugin-lodash o babel-plugin-import transforman los imports completos en imports selectivos durante el build.

Code splitting divide el bundle JavaScript en múltiples chunks que se cargan bajo demanda. La técnica principal es el import() dinámico: en lugar de import { Gallery } from './Gallery' (carga estática, incluido en el bundle principal), se usa const { Gallery } = await import('./Gallery') (carga dinámica, genera un chunk separado que se descarga solo cuando se necesita).

Los frameworks modernos implementan code splitting por defecto en las rutas. Next.js genera un chunk separado para cada página. Astro va más allá con la arquitectura de Islands, donde cada componente interactivo se carga como un chunk independiente solo cuando es visible en el viewport (client:visible). Esta granularidad convierte el code splitting de una optimización manual en un comportamiento por defecto del framework.

La combinación de tree shaking + code splitting + lazy loading de componentes puede reducir el payload inicial de JavaScript un 50-70% en aplicaciones de tamaño medio. Para un SPA con 1,2 MB de JavaScript total, esto significa pasar de enviar 1,2 MB en la carga inicial a enviar 350-500 KB, con el resto cargado progresivamente según la navegación del usuario.

Defer vs async: cuándo usar cada atributo en scripts

Los atributos defer y async controlan cuándo y cómo el navegador descarga y ejecuta scripts externos. Usarlos correctamente puede mejorar el FCP y el INP sin modificar el código JavaScript en sí. Usarlos incorrectamente puede romper la funcionalidad de la página.

Sin defer ni async (script normal): el navegador detiene el parseo del HTML cuando encuentra el <script>, descarga el archivo, lo ejecuta, y solo entonces reanuda el parseo. Esto es render-blocking y es la causa principal de que scripts en el <head> retrasen el FCP. Solo debe usarse para scripts que deben ejecutarse antes de que el usuario vea cualquier contenido —casos extremadamente raros como feature detection o polyfills críticos.

Con async: el navegador descarga el script en paralelo con el parseo del HTML, pero lo ejecuta inmediatamente cuando la descarga termina, pausando el parseo si es necesario. El orden de ejecución entre múltiples scripts async no está garantizado: el primero que termine de descargar se ejecuta primero. Esto significa que async es inadecuado para scripts que dependen de otros scripts. Es apropiado para scripts completamente independientes como analytics (Google Analytics, Plausible) o widgets de terceros sin dependencias.

Con defer: el navegador descarga el script en paralelo con el parseo del HTML y lo ejecuta solo después de que el DOM esté completamente construido, justo antes del evento DOMContentLoaded. El orden de ejecución entre múltiples scripts defer se mantiene: se ejecutan en el orden en que aparecen en el HTML. Defer es la opción correcta para la mayoría de los scripts de aplicación que necesitan acceso al DOM.

La recomendación práctica es: defer para todos los scripts de la aplicación que acceden al DOM, async para scripts independientes de terceros (analytics, pixels), y ni defer ni async solo para polyfills críticos que deben estar disponibles antes de que cualquier otro script se ejecute.

Un error frecuente es colocar scripts con defer al final del <body> pensando que así se cargan después. Con defer en el <head>, el script se descarga en paralelo con el parseo del HTML desde el principio, pero se ejecuta al final. En el <body> sin defer, el script se descarga y ejecuta secuencialmente cuando el parser llega a esa posición. El primer caso es más rápido porque la descarga empieza antes.

La diferencia de rendimiento es medible. En un test con WebPageTest simulando 4G, mover tres scripts de terceros (GTM, Facebook Pixel, Hotjar) de carga normal a defer mejoró el INP de la página de 380 ms a 180 ms, cruzando el umbral de “bueno” de 200 ms de Google. El FCP también mejoró 0,4 segundos porque los scripts dejaron de bloquear el hilo principal durante la carga inicial.

Métricas: cuánto puedes mejorar eliminando JS no usado

La cuantificación del impacto potencial antes de implementar cambios permite priorizar intervenciones y justificar la inversión de tiempo de desarrollo. Las métricas clave para evaluar JavaScript no utilizado son: peso total de JS, porcentaje de código no ejecutado, tiempo de procesamiento en hilo principal, y las métricas de Core Web Vitals afectadas.

Peso total de JavaScript se mide filtrando por tipo “Script” en la pestaña Network de DevTools. El peso transferido (comprimido) es lo que impacta el tiempo de descarga. El peso sin comprimir (parsed) es lo que impacta el tiempo de parseo y compilación. Para la web mediana, el peso transferido es de 500 KB y el peso parseado es de 1,5-2 MB (la ratio de compresión gzip/brotli para JavaScript es típicamente de 3:1 a 4:1).

Porcentaje de código no ejecutado lo proporciona Chrome DevTools Coverage. El benchmark es: por debajo del 30% de código no utilizado es aceptable, entre 30% y 50% requiere atención, por encima del 50% es un problema crítico. La mediana de sitios web tiene un 45% de JavaScript no utilizado, según los datos de Coverage recopilados por Web Almanac.

Tiempo de procesamiento en hilo principal se visualiza en el Performance tab de DevTools. La categoría “Scripting” muestra cuántos milisegundos del tiempo de carga se dedican a compilar y ejecutar JavaScript. Para páginas con LCP por encima de 2,5 segundos, si Scripting supera los 500 ms, la reducción de JavaScript es la intervención prioritaria.

Los resultados documentados de eliminar JavaScript no utilizado son consistentes. Un estudio de caso de web.dev sobre un sitio de noticias mostró que reducir el JavaScript de 800 KB a 350 KB mejoró el LCP de 3,8 a 2,1 segundos en dispositivos móviles de gama media. El INP mejoró de 350 ms a 120 ms. El tráfico orgánico móvil creció un 15% en las ocho semanas posteriores a la implementación.

Para e-commerces con problemas de JavaScript SEO, la reducción de JavaScript tiene un beneficio doble: mejora los Core Web Vitals para usuarios reales y reduce el coste computacional para Googlebot, que necesita ejecutar JavaScript para indexar páginas con Client-Side Rendering. Menos JavaScript significa indexación más rápida y más completa.

El orden de intervención recomendado es: primero eliminar scripts de terceros no esenciales (mayor impacto con menor riesgo), después implementar defer/async en los restantes (impacto medio, riesgo bajo), luego aplicar code splitting por ruta (impacto alto, riesgo medio), y finalmente optimizar tree shaking de dependencias npm (impacto variable, riesgo bajo). Esta secuencia maximiza el retorno por unidad de esfuerzo y reduce la probabilidad de regresiones funcionales.

Comparativa: eliminar JavaScript no utilizado

Característica eliminar JavaScript no utilizadoAlternativa
¿Cuánto JavaScript es demasiado para una web? No hay un límite absoluto, pero la recomendación de Google es mantener el JavaScript inicial (antes de interacción) por debajo de 300 KB comprimidos. Según HTTP Archive, la mediana es de 500 KB, lo que significa que la mayoría de los sitios envían más JavaScript del necesario. El impacto real depende del dispositivo: en un iPhone 15 Pro, 500 KB de JS se procesan en 200 ms; en un Android de gama media, el mismo JS tarda 800-1200 ms.-
¿Los plugins de WordPress añaden mucho JavaScript? Sí, significativamente. Un estudio de HTTPArchive muestra que los sitios WordPress cargan una media de 600 KB de JavaScript, un 20% más que la media general. Cada plugin puede añadir entre 20 y 200 KB de JS, a menudo en todas las páginas aunque solo se use en algunas. Los mayores culpables suelen ser sliders, builders (Elementor, Divi), plugins de analytics y widgets de chat en vivo.-
¿Cómo cargo scripts de terceros sin dañar el rendimiento? Tres estrategias ordenadas por impacto: primera, añadir el atributo defer a los scripts que no son críticos para el renderizado inicial; segunda, cargar los scripts de terceros con la Facade Pattern, donde un placeholder ligero se muestra hasta que el usuario interactúa y entonces se carga el script completo; tercera, usar un tag manager con reglas de activación por página para cargar solo los scripts necesarios en cada URL.-

Puntos clave

  • La página web media envía 500 KB de JavaScript, de los cuales entre el 40% y el 70% no se ejecuta en la página actual
  • Cada 100 KB de JavaScript adicional añade entre 150 y 350 ms de procesamiento en dispositivos móviles de gama media
  • Tree shaking elimina código muerto automáticamente durante el build, pero requiere módulos ES (import/export) para funcionar
  • Code splitting con import() dinámico permite cargar JavaScript bajo demanda, reduciendo el payload inicial un 30-60%
  • Defer y async en scripts de terceros pueden mejorar el INP (Interaction to Next Paint) entre 100 y 300 ms

Fuentes y referencias

  1. Remove Unused JavaScript (developer.chrome.com)
  2. Tree Shaking (webpack.js.org)
  3. Web Almanac: JavaScript (almanac.httparchive.org)