Voltar ao blog

Como Adicionar Analytics Focado em Privacidade a uma App SvelteKit

O SvelteKit 2 intercepta a navegação client-side de forma diferente dos outros frameworks. Este é o padrão correto para rastrear mudanças de rota sem cookies ou banners de consentimento.

O SvelteKit 2 quebrou silenciosamente a maioria das integrações de analytics existentes. Uma tag <script> simples que funcionava bem no SvelteKit 1 só dispara eventos em recarregamentos completos no SvelteKit 2, perdendo toda navegação client-side. A solução habitual — interceptar history.pushState — também não funciona, porque o SvelteKit intercepta a navegação no nível do router antes de chamar pushState. O tracker do Monoid lida com isso corretamente sem nenhuma configuração adicional.

Por que a abordagem habitual falha

Scripts de analytics tradicionais detectam mudanças de rota aplicando monkey-patching em history.pushState e history.replaceState. O router do SvelteKit chama suas próprias primitivas internas de navegação e só invoca pushState como efeito colateral, depois que a transição de rota já está em andamento. No momento em que o pushState modificado é executado, a nova rota pode não ter sido renderizada por completo, e em alguns tipos de navegação (chamadas programáticas a goto(), cliques em <a> com prefetch) o timing gera duplicatas ou eventos perdidos.

Esta é a causa raiz do problema amplamente relatado em que o Google Analytics GA4 registra apenas o primeiro pageview no carregamento inicial em aplicações SvelteKit 2.

A abordagem correta

Adicione o script do tracker do Monoid uma vez no seu +layout.svelte raiz usando <svelte:head>:

<svelte:head>
  <script
    async
    src="https://api.monoid.website/tracker.min.js"
    data-site-id="SEU_SITE_ID"
  ></script>
</svelte:head>

O próprio listener de DOMContentLoaded do tracker é disparado no carregamento inicial da página. Para navegações client-side subsequentes, o hook de history.pushState do tracker é executado após a navegação interna do SvelteKit ter resolvido — o que significa que cada rota, incluindo a primeira, é registrada exatamente uma vez, sem duplicatas ou omissões.

Para verificar, abra a aba de Rede no DevTools e observe as requisições POST para /collect. Você deve ver uma por mudança de rota.

Separar o tráfego de staging

Projetos SvelteKit geralmente rodam em localhost durante o desenvolvimento. Registre um site separado e vincule seu site_id a uma variável de ambiente para que o tráfego de desenvolvimento nunca contamine as métricas de produção:

<script>
  const siteId = import.meta.env.VITE_MONOID_SITE_ID
</script>

<svelte:head>
  {#if siteId}
    <script
      async
      src="https://api.monoid.website/tracker.min.js"
      data-site-id={siteId}
    ></script>
  {/if}
</svelte:head>

Defina VITE_MONOID_SITE_ID no .env.local para desenvolvimento (ou deixe sem definir para suprimir o tracking) e no ambiente de deploy para produção. O bloco {#if siteId} garante que o script do tracker nunca seja injetado quando a variável não estiver definida.

O que você não precisa

Sem banner de consentimento de cookies. Sem atualização de política de cookies. Sem verificação de navigator.cookieEnabled. O endpoint de coleta deriva um hash de visitante diário a partir do endereço IP, do User-Agent, de um segredo do lado do servidor e da data atual:

visitor_hash = SHA-256(IP + UA + SALT_SECRET + YYYY-MM-DD)

O hash é redefinido a cada 24 horas e não pode ser revertido para recuperar o IP ou o User-Agent. Nada é armazenado no dispositivo do visitante. Isso significa que a integração não aciona os requisitos de consentimento da ePrivacy ou da PECR — não há nada sobre o que consentir.

Verificando no dashboard

Após o deploy, abra seu dashboard de analytics e navegue entre várias rotas no seu site em produção. Cada navegação deve produzir uma nova entrada de pageview com o caminho correto. Se você ver apenas uma entrada independentemente da navegação, verifique se o script está carregado no layout raiz e se VITE_MONOID_SITE_ID está configurado no ambiente de produção.

O adaptador SvelteKit que você usa (Cloudflare, Vercel, Node, estático) não afeta a coleta de analytics, já que o tracking ocorre inteiramente no navegador após a hidratação.