TL;DR, Respuesta rápida
9 min de lecturaUna aplicación de una sola página carga un único documento HTML y cambia de pantalla con JavaScript, así que una etiqueta que solo se dispara al cargar la página registra una sola página vista en toda la visita. El tracking de SPA lo corrige con una página vista virtual en cada cambio de ruta, ya sea a partir de los eventos de la History API (el ajuste "Page changes based on browser history events" de GA4 escucha pushState, popState y replaceState) o de llamadas page_view manuales. El doble conteo viene de ejecutar dos de estos mecanismos a la vez; las páginas vistas que faltan vienen de routers que no tocan la URL o de páginas vistas enviadas antes de que se actualice la URL.
¿Qué es el tracking de SPA?
En una aplicación de una sola página, el tracking de SPA consiste en registrar una página vista virtual cada vez que la app cambia de pantalla con JavaScript, porque el navegador carga el documento HTML una sola vez y nunca hace una nueva carga de página en los cambios de ruta posteriores. Google describe las SPA como "websites that load an HTML document once and fetch any additional content using JavaScript APIs," y plantea el objetivo sin rodeos: "The key to measuring page views from SPAs correctly is to count page views for each screen a user interacts with and get the page referrer right, so you can correctly trace the user journey."
Una página vista virtual es un evento de página vista que tu herramienta de analítica envía sin una carga de página real. El navegador nunca pide un documento nuevo, pero el informe muestra una página nueva con su propia URL y su propio referrer, igual que si lo hubiera hecho.
¿Por qué una etiqueta de analítica estándar no registra las páginas vistas de una SPA?
Una etiqueta de analítica estándar no registra las páginas vistas de una SPA porque envía su página vista cuando el script se ejecuta por primera vez, y en una SPA el script se ejecuta una vez por visita. Un visitante que llega a /, abre /pricing y después /signup mediante enlaces internos de la app ha tomado decisiones en tres pantallas, mientras que una etiqueta que solo mide la carga registra una página vista de /.
El daño va más allá del recuento de páginas vistas. Google señala que sin páginas vistas virtuales "Google Analytics treats the SPA as a single page, leading to skewed metrics," y que "all engagement time is attributed to the initial page load, making it impossible to analyze time spent on individual screens." Los clics y los scrolls también se archivan bajo la URL de llegada. La guía de páginas vistas frente a sesiones frente a usuarios explica por qué un número bajo de páginas vistas por sesión en una SPA es una señal de tracking antes que una señal de comportamiento.
![]()
¿Cómo indica la History API un cambio de ruta?
La History API indica un cambio de ruta mediante métodos que llama tu router y eventos que dispara el navegador, y las dos cosas no se corresponden una a una. Ese desajuste es la razón por la que los trackers tienen que escuchar en más de un sitio.
| Cambio | Qué lo provoca | Evento del navegador |
|---|---|---|
history.pushState() | Tu router, en un enlace interno de la app | Ninguno |
history.replaceState() | Tu router, cuando reescribe la URL actual | Ninguno |
| Botón atrás o adelante | El visitante | popstate |
history.back(), forward(), go() | Tu código | popstate |
Cambio de hash (#section) | Un enlace o código que cambia el fragmento | hashchange |
MDN es explícito sobre las dos primeras filas: "just calling history.pushState() or history.replaceState() won't trigger a popstate event." Un tracker que solo escucha popstate ve los clics en atrás y adelante y se pierde cada navegación hacia delante por tus propios enlaces. Los trackers lo resuelven envolviendo u observando pushState y replaceState directamente.
¿Cómo registra GA4 las aplicaciones de una sola página?
GA4 registra las aplicaciones de una sola página mediante un ajuste de medición mejorada que envía un page_view cuando cambia el historial del navegador. El centro de ayuda de Google describe el activador de page_view como "each time the page loads or the browser history state is changed by the active site," y dice que el ajuste avanzado "listens for pushState, popState, and replaceState." Google recomienda esta vía "If your SPA uses the History API, specifically the pushState() and replaceState() method to update screens."
Para activarlo:
- En Google Analytics, abre Admin.
- En Data collection and modification, haz clic en Data streams y después en tu flujo web.
- En Enhanced measurement, activa el interruptor.
- Haz clic en el icono de ajustes y, en Page views, haz clic en Show advanced settings.
- Activa tanto Page loads como Page changes based on browser history events.
- Guarda.
La otra vía es manual. Google indica "Custom events" para los sitios en los que las pantallas se renderizan sin cambiar la URL, como una app que usa objetos DocumentFragment. En ese caso envías tú mismo gtag('event', 'page_view', { page_title: '...', page_location: '...' }) desde tu router.
¿Qué provoca el doble conteo en el tracking de SPA?
El doble conteo en el tracking de SPA se produce cuando dos mecanismos envían cada uno una página vista para el mismo cambio de ruta. Las configuraciones habituales que lo provocan:
- Google Tag Manager más los eventos de historial de GA4. La advertencia de Google: "If you configure your tags using Google Tag Manager, don't enable automatic history tracking in Google Analytics, as this can lead to double-counting page views." Un activador History Change de GTM ya se dispara "when the URL fragment changes or when a site uses the HTML5 pushstate API."
- Llamadas page_view manuales más el ajuste de historial.
send_page_view: falseno detiene las páginas vistas basadas en el historial. Google afirma: "If Enhanced Measurement is enabled, Google Analytics will sendpage_viewevents based on browser history changes even if you setsend_page_view: false." Desactiva Page changes based on browser history events cuando tu router envíe sus propias páginas vistas. - replaceState usado para filtros o pestañas. GA4 incluye replaceState entre los cambios de historial que escucha, así que una lista de productos que escribe
?sort=priceen la URL conreplaceStatepuede generar páginas vistas en cada cambio de filtro. Pruébalo en DebugView antes de fiarte de los recuentos de páginas en pantallas con muchos filtros. - Dos etiquetas de Google con el mismo ID. Un fragmento de gtag escrito a mano y una etiqueta de Google en GTM que apuntan al mismo ID de medición envían cada uno sus propios eventos.
¿Qué provoca que falten páginas vistas en el tracking de SPA?
Las páginas vistas que faltan en el tracking de SPA vienen de cambios de ruta que nunca llegan a la History API y de páginas vistas enviadas en el momento equivocado.
- Routers que nunca cambian la URL. Una app que intercambia componentes en memoria no le da nada que oír a un listener del historial. Usa la vía de los eventos personalizados.
- Routing por hash con un tracker que solo mira el historial. Cambiar
#/pricingdisparahashchange. Comprueba si tu herramienta cuenta los cambios de fragmento y configúrala si no lo hace. - Páginas vistas enviadas antes de que se actualice la URL. Un
page_viewque se dispara antes depushStateregistra elpage_locationantiguo. En DebugView, Google aconseja comparar cadapage_view"with the precedingpage_viewevent to check if the page referrer and page location have been updated correctly." - Referrers desfasados. El ejemplo de evento personalizado de Google guarda la URL anterior y la pasa como
page_referrer, porque eldocument.referrerdel propio navegador se queda fijo en el referrer de llegada original durante toda la visita.
Hay una trampa relacionada fuera del router. web.dev señala que la caché de avance y retroceso "doesn't work for 'soft navigations' within a single-page app," así que un clic en atrás dentro de tu app es un evento de historial, mientras que un clic en atrás que vuelve a tu app desde otro sitio puede restaurar la página entera desde la memoria sin disparar ningún evento de historial.
![]()
¿Cómo compruebas que el tracking de SPA funciona?
Compruebas el tracking de SPA navegando por la app con el modo de depuración activado y verificando que cada pantalla nueva produce exactamente una página vista con la ubicación y el referrer correctos. La comprobación de Google consiste en activar el modo de depuración, "click through your single-page application," y confirmar "a new page_view event in DebugView" en cada pantalla virtual. Después repítelo con los botones atrás y adelante, un cambio de filtro y un enlace con hash, porque esos son los casos que duplican o pierden páginas vistas. La lista de verificación de analítica aplica la misma idea a cualquier tracker, y la guía sobre la duración media de la sesión muestra lo que un tracking de SPA roto hace con las cifras de tiempo en el sitio.
Panel en tiempo real
Seguimiento de objetivos
Rastreo sin cookies
Dos arreglos más pequeños ayudan a que las cifras se sostengan. Restablece la posición de scroll en cada cambio de ruta con window.scrollTo(0, 0) para que la profundidad de scroll corresponda a la pantalla nueva y no a la anterior, y envía la página vista solo después de que el router haya actualizado la URL y el título.
¿Cómo gestiona Flowsery la navegación en una SPA?
Flowsery registra automáticamente la navegación en una SPA, así que un cambio de ruta de React Router o Next.js aparece como su propia página vista sin código extra. La documentación del SDK de npm de Flowsery incluye "pageviews, including SPA navigation" en el tracking automático, y su FAQ de precios afirma que Flowsery "counts a pageview each time a visitor loads a page on your site, including single-page app navigations." Para apps que enrutan con fragmentos de URL, el SDK de npm tiene una opción hashMode, y la documentación del script describe una versión del tracker para SPA con routing por hash. Si tu app cambia de pantalla sin cambiar la URL, el SDK expone trackPageview() para que puedas enviar una desde tu propio código. La página de analítica para React explica cómo mide Flowsery las rutas del lado del cliente, los eventos personalizados, los embudos y los ingresos en apps de React.
Preguntas frecuentes
¿Qué es una página vista virtual?
Una página vista virtual es un evento de página vista enviado sin una carga de página real, que se usa cuando una SPA cambia de pantalla mediante JavaScript. Lleva su propia ubicación de página y su propio referrer, así que los informes tratan cada pantalla como una página distinta. Sin ella, una visita a una SPA aparece como una única vista larga de la página de llegada.
¿GA4 registra automáticamente los cambios de ruta de una SPA?
Sí, si la medición mejorada está activada y Page changes based on browser history events está habilitado en los ajustes avanzados de Page views. Ese ajuste escucha pushState, popState y replaceState. No sirve para apps que cambian de pantalla sin cambiar la URL.
¿Por qué GA4 cuenta dos veces cada página vista de la SPA?
Hay dos mecanismos enviando páginas vistas para el mismo cambio. Las parejas habituales son los activadores de historial de GTM con los eventos de historial de GA4, o las llamadas page_view manuales con los eventos de historial de GA4. Desactiva uno de los dos; Google afirma que send_page_view: false no detiene las páginas vistas basadas en el historial.
¿pushState dispara popstate?
No. MDN afirma que llamar a history.pushState() o history.replaceState() no dispara un evento popstate. Solo lo disparan acciones del navegador como los botones atrás y adelante, o llamadas como history.back().
¿Cómo debe funcionar page_referrer en una SPA?
Cada página vista virtual debe llevar como referrer la URL de la pantalla anterior. Guarda la URL antigua antes del cambio de ruta y pásala como page_referrer junto con el nuevo page_location. Si no, cada pantalla informa del referrer de llegada original.
¿La caché de avance y retroceso afecta al tracking de SPA?
No en las navegaciones dentro de la app. web.dev afirma que la bfcache no funciona con las navegaciones suaves dentro de una SPA. Sí se aplica cuando un visitante sale de tu app y vuelve con el botón atrás, lo que puede restaurar la página entera desde la memoria sin una carga nueva si la página cumple los requisitos.
¿Cómo activo el tracking de SPA en GA4?
Abre Admin, haz clic en Data streams, elige tu flujo web y activa Enhanced measurement. Después haz clic en el icono de ajustes, abre Show advanced settings en Page views y activa tanto Page loads como Page changes based on browser history events. Guarda. Esta vía funciona cuando tu router usa pushState y replaceState.
¿Debo activar el seguimiento del historial de GA4 si uso Google Tag Manager?
Déjalo desactivado. Google advierte de que usar ambos puede provocar un doble conteo de páginas vistas. Un activador History Change de GTM ya se dispara cuando cambia el fragmento de la URL o cuando el sitio usa la API pushstate de HTML5.
¿Cómo registro rutas con hash como #/pricing en una SPA?
Cambiar el fragmento dispara hashchange, así que un tracker que solo vigila la History API puede no verlo. Comprueba si tu herramienta cuenta los cambios de fragmento y configúrala si no lo hace. El SDK de npm de Flowsery tiene una opción hashMode, y su documentación del script describe una versión del tracker para enrutado con hash.
¿Cómo compruebo que mi SPA envía una página vista por pantalla?
Activa el modo de depuración, navega por la app y busca en DebugView un nuevo evento page_view por cada pantalla. Compara cada uno con el page_view anterior para confirmar que la ubicación de la página y el referrer se actualizaron. Repite con los botones de avance y retroceso, un cambio de filtro y un enlace con hash.
¿Te resultó útil este artículo?
¡Cuéntanos qué opinas!
Vernos más en Google
Un clic marca Flowsery como fuente preferida y nuestros artículos aparecen más arriba en tus Noticias destacadas, el modo IA y los resúmenes con IA.
Antes de irte...
Flowsery
Analítica orientada a ingresos para tu sitio web
Rastrea cada visitante, fuente y conversión en tiempo real. Simple, potente y sin cookies.
Panel en tiempo real
Seguimiento de objetivos
Rastreo sin cookies
Términos relacionados del glosario


Por qué tu tasa de rebote de GA4 es la tasa de interacción al revés
Google mide la tasa de rebote de GA4 como la proporción de sesiones sin interacción. Cómo la define, en qué difiere de Universal Analytics y dónde añadirla.


Qué cuenta el informe de páginas de destino de GA4 y dónde se queda en blanco
En Reports, el informe de páginas de destino de GA4 lista la primera página de cada sesión. Dónde está, sus métricas, por qué sale (not set) y la fuente.


Cómo registra la búsqueda en el sitio de GA4 lo que escriben los visitantes
Configura la búsqueda en el sitio de GA4 con la medición mejorada, añade tus parámetros de consulta, halla la dimensión Search term y mide sin URL.


Cómo definir propiedades de usuario de GA4 y usarlas en informes
Define propiedades de usuario de GA4 con gtag o Tag Manager, regístralas como dimensiones personalizadas de usuario y no pases del límite de 25.


Cómo vincular Search Console con GA4 y publicar sus informes
Aprende a vincular Search Console con GA4 en Admin, qué roles necesitas, cómo publicar los dos informes en Library y qué datos no se pueden cruzar.


Qué hace la bfcache con la navegación hacia atrás y tus páginas vistas
La bfcache, o caché de avance y retroceso, restaura una página desde memoria al ir atrás o adelante. Qué páginas entran y por qué la analítica las pierde.
Artículos relacionados


Cómo crear métricas calculadas de GA4 con una cuota de cinco huecos
Google limita las métricas calculadas de GA4 a 5 por propiedad estándar. Aprende la sintaxis, las unidades, dónde aparecen y qué no admite una fórmula.


Cómo configurar la agrupación de contenido de GA4 con un solo parámetro
Configura la agrupación de contenido de GA4 con el parámetro content_group en gtag o Tag Manager, lee la dimensión Content group y evita filas (not set).


Cómo añadir anotaciones de Google Analytics en GA4 y llevar un registro de cambios
En GA4, las anotaciones de Google Analytics fijan notas con fecha en los gráficos. Quién las crea, los límites de 60, 150 y 1,000, y su uso como registro.