Interaction Contentful Paint: cómo medir por fin lo que pasa dentro de una SPA
webdevelopment 14 de agosto de 2026 · Mintec

Interaction Contentful Paint: cómo medir por fin lo que pasa dentro de una SPA

Chrome 151 (julio 2026) lanzó dos entradas de rendimiento que cierran el punto ciego de las SPAs: interaction-contentful-paint y soft-navigation. Te mostramos cómo las usamos en Mintec para atribuir INP y LCP a rutas concretas.

Interaction Contentful Paint: cómo medir por fin lo que pasa dentro de una SPA

Chrome 151 (estable desde el 28 de julio de 2026) lanzó dos entradas de rendimiento — interaction-contentful-paint y soft-navigation — que cierran el punto ciego histórico de las aplicaciones de una sola página: hasta ahora era imposible medir Core Web Vitals por ruta dentro de una SPA. Con estas APIs puedes atribuir la latencia de una interacción a un paint de contenido concreto, incluso cuando ese contenido llega por un fetch asíncrono.

Esto no es una mejora cosmética de DevTools. Es la primera vez que el navegador entiende "navegación" dentro de un documento que nunca se recargó. Y cambia cómo diagnosticamos, y discutimos, el rendimiento de las SPAs.

El punto ciego que existía desde hace diez años

Las métricas de Core Web Vitals se diseñaron alrededor de la idea de "página": el usuario pide una URL, el navegador la pinta y la medición empieza y termina ahí. Las SPAs rompieron ese modelo hace más de una década: el usuario hace clic, JavaScript cambia el contenido, la URL cambia con history.pushState y no hay ninguna recarga que reinicie el cronómetro. Como documentan los propios ingenieros de Chrome, la arquitectura de aplicación de una sola página nunca estuvo completamente soportada por las métricas de Core Web Vitals.

En la práctica, esto significaba dos problemas para cualquiera que mantenga un dashboard, un checkout de varios pasos o una app de cliente:

  1. El INP de la página completa mezclaba todas las rutas. Un INP de 340ms en rojo no te decía si el culpable era el login, el catálogo o el carrito. Empezábamos cada auditoría adivinando.
  2. El LCP de una "página" era el de la primera carga. El LCP de la ruta /productos/123 que el usuario abrió con un clic interno jamás se medía. Rendimiento de navegación = invisible.

Con LoAF (Long Animation Frames) pudimos encontrar scripts culpables dentro de una interacción, pero seguíamos sin poder responder la pregunta de granularidad: ¿qué ruta es la lenta?

Qué trae Chrome 151 exactamente

Chrome 151 añade dos tipos de entrada al performance timeline:

  • interaction-contentful-paint (ICP): se emite cuando una interacción del usuario produce un paint de contenido nuevo dentro de las regiones del DOM que esa interacción modificó. El nombre completo del proyecto era "Interaction to Contentful Paint", y es deliberadamente distinto de INP: no mide cuándo pinta el navegador algo, sino cuándo pinta contenido nuevo en el lugar correcto. Funciona incluso si el contenido llega por un fetch asíncrono que termina 400ms después de la interacción.
  • soft-navigation: se emite cuando se detecta una navegación suave — un cambio de estado iniciado por el usuario que actualiza la URL y el historial sin recargar el documento. Cada entrada incluye un navigationId, la nueva URL y el interactionId de la interacción que la inició, y establece un nuevo origen de tiempo para atribuir las entradas de rendimiento posteriores (first-paint, first-contentful-paint, largest-contentful-paint, event, layout-shift) a esa navegación.

Además, la entrada soft-navigation expone getLargestInteractionContentfulPaint(), que recupera el ICP más grande de esa navegación: el equivalente funcional del LCP para una ruta de SPA. La feature llega sin flag, disponible para todos los sitios desde la versión 151, después de un origin trial que terminó en Chrome 147.

INP vs ICP vs soft-navigation vs LoAF: cuándo usar cada uno

La confusión más común es tratar a ICP como "el nuevo INP". No lo es: son herramientas complementarias con granularidades distintas.

HerramientaQué mideGranularidadSoporteCuándo usarla
INPTiempo de interacción a next paint (una cifra por página)Página completaChrome, Safari, Firefox (CWV)Monitoreo global, umbrales de negocio
interaction-contentful-paintPaints de contenido en la región interactuadaInteracción individualChrome 151+Atribución de rutas y componentes en SPAs
soft-navigationCambios de URL/estado en el mismo documento iniciados por el usuarioRutaChrome 151+Baseline de rendimiento por ruta, LCP de navegación interna
LoAFFrames de animación >50ms y los scripts que los causanFrame/tareaChrome/ChromiumCausa raíz dentro de una interacción lenta

Nuestro flujo quedó así: INP para saber que algo está mal, soft-navigation para saber qué ruta, ICP para saber qué paint, LoAF para saber qué script. Cuatro niveles de zoom que antes no existían juntos.

Cómo instrumentarlo en producción

No necesitas una librería. Con un PerformanceObserver en el bundle del cliente (o en un script inline si quieres capturar desde el primer momento):

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.entryType === "soft-navigation") {
      console.log("Soft navigation:", entry.name, entry.navigationId);
      // LCP equivalente de la ruta
      entry.getLargestInteractionContentfulPaint?.().then((icp) => {
        console.log("Ruta pintada en:", icp?.startTime, "ms");
      });
    }
    if (entry.entryType === "interaction-contentful-paint") {
      console.log("ICP:", entry.startTime, "interaction:", entry.interactionId);
    }
  }
});
observer.observe({ type: ["soft-navigation", "interaction-contentful-paint"], buffered: true });

En un proyecto Next.js de un cliente con un panel de administración, esta instrumentación nos dio en una semana lo que no habíamos logrado en un mes de perfiles manuales: la ruta de órdenes tenía un ICP mediano de 780ms porque el fetch de la tabla esperaba a un chunk de charting que no se necesitaba en esa vista. El INP de página completa lo escondía porque el resto de rutas eran rápidas. Con el dato por ruta, diferimos el chunk y el ICP bajó a 210ms sin tocar nada más.

El marco de trabajo que usamos (tres pasos)

  1. Instrumentar y guardar por ruta. Observa ambas entradas, guarda navigationId + URL + startTime de ICP en tu RUM (o en un log de desarrollo). Una semana de datos es suficiente para un baseline.
  2. Ordenar rutas por ICP mediano. No por INP general. Las rutas con peor ICP son donde el usuario percibe que "la app no responde", aunque el INP agregado luzca aceptable. Ese desfase es exactamente lo que estas métricas revelan.
  3. Arreglar por ruta, no por componente global. Cada ruta lenta se ataca con sus propios chunks, fetches y prioridades. Verifica con el mismo observer que el ICP de esa ruta bajó del percentil 75 de tu baseline (nosotros usamos 300ms como objetivo inicial, alineado con el criterio de "good" de INP).

Qué cambia para la decisión SPA vs MPA (nuestra opinión)

Esto no revive el debate "SPA vs MPA" — pero lo aclara. Durante años, parte del argumento contra las SPAs era "no se pueden medir". Ese argumento murió en julio de 2026. Ahora la pregunta es de negocio: si tu SPA tiene rutas con ICP malo, tienes la herramienta para encontrarlas y arreglarlas. Y si estás decidiendo arquitectura, esta es otra razón para preferir renders híbridos tipo Astro con islas: menos superficie de soft navigation que medir, porque la mayor parte del sitio se comporta como MPA.

El caveat importante: por ahora esto es Chrome/Chromium. Safari y Firefox no lo soportan, así que no lo uses como única fuente de verdad en RUM — úsalo como capa de diagnóstico profundo sobre el INP de campo que ya medimos en todos los navegadores. La estrategia que recomendamos en nuestra guía de INP no cambia: los umbrales y el flujo de depuración con LoAF y scheduler.yield() siguen siendo la base. Lo que cambia es que ahora puedes responder "¿qué ruta?" con datos, no con corazonadas.

La conclusión es simple: el navegador por fin sabe lo que pasa dentro de una SPA, y nosotros ya no tenemos excusa para no saberlo también.

Preguntas Frecuentes

¿Qué es Interaction Contentful Paint (ICP)?

Es una entrada de rendimiento que Chrome 151 lanzó en julio de 2026. Mide los nuevos paints de contenido que ocurren dentro de las regiones del DOM modificadas por una interacción del usuario, lo que permite atribuir la respuesta visual a interacciones concretas — incluso cuando el contenido llega por un fetch asíncrono.

¿Cuál es la diferencia entre INP e ICP?

INP mide el tiempo total desde la interacción hasta el siguiente paint y produce una sola cifra por página. ICP mide paints de contenido específicos dentro de la región interactuada y puede asociarse a soft navigations, dando granularidad por ruta que el INP de página completa no ofrece en aplicaciones de una sola página.

¿En qué navegadores funcionan soft-navigation e interaction-contentful-paint?

Por ahora solo en Chrome y Chromium, desde la versión 151 (28 de julio de 2026). Safari y Firefox todavía no los soportan, así que conviene usarlos como herramienta de diagnóstico profundo en Chrome y mantener el INP/LCP de campo como fuente de verdad transversal.

¿Qué es una soft navigation?

Es un cambio de estado del documento iniciado por una acción del usuario que actualiza la URL e inserta entradas en el historial sin recargar la página — exactamente lo que hace una SPA en cada cambio de ruta. Chrome define sus límites con criterios precisos (interacción del usuario, cambio de URL, actualización del historial) para poder medir Core Web Vitals en ese tipo de navegación.

Artículos Relacionados