Microsoft Clarity em React SPA só grava sessões quando o stub window.clarity existe antes do script clarity.ms/tag. Sem esse bootstrap oficial, o tag carrega no Network mas a gravação falha em silêncio — e o dashboard fica preso em "Quase lá" com zero sessões. O diagnóstico mais rápido é typeof window.clarity no Console de produção. Corrija com stub oficial antes do script remoto e valide beacons collect após deploy. Gravações aparecem minutos depois da primeira visita real no browser.
Introdução
Você configurou o Project ID, exportou a variável de ambiente, fez deploy e viu o script no Network. Mesmo assim, o Microsoft Clarity não registra sessões. O onboarding não avança. A API e o MCP retornam zero tráfego.
Esse cenário é comum em Single Page Applications (SPAs) construídas com React, Vite ou Next.js. O time acha que a integração está certa porque o arquivo aparece carregado. Na prática, nada chega ao dashboard.
O frustrante é o custo de oportunidade: sem gravações, decisões de UX ficam no achismo. Times de produto debatem se o formulário está longo demais — mas ninguém viu o usuário abandonar na terceira pergunta. Marketing pede heatmap da landing — e recebe telas vazias. O Clarity é gratuito e poderoso; o bug não é falta de recurso, é meia instalação.
Neste artigo você vai ver: por que o erro acontece, como diagnosticar em dois minutos no DevTools, o padrão correto de instalação e o checklist para confirmar que as gravações chegaram. O foco é prático — para quem mantém site React em produção, não para quem só copia snippet de CMS.
Sintomas: dashboard em "Quase lá" e zero sessões
Os sinais aparecem sempre na mesma ordem:
- Clarity Dashboard — onboarding preso em "Quase lá" / "Almost there", mesmo após visitas reais ao site.
- Recordings — aba vazia ou sem sessões novas.
- DevTools → Network — request para
https://www.clarity.ms/tag/{seu-project-id}com status 200. - DevTools → Console —
typeof window.clarityretorna"undefined". - Heatmaps e funis — sem dados após dias de tráfego.
O ponto crítico: o script carrega, mas não executa a gravação. Por isso o problema passa despercebido em revisões de deploy que só checam se o tag está presente.
Equipes de produto costumam descobrir o bug quando tentam cruzar Clarity com funis de conversão: PostHog ou GA4 mostram pageviews, mas Clarity não mostra gravações equivalentes. A discrepância não é lag — é integração quebrada desde o primeiro deploy.
Se você migrou de WordPress para React/Vite e copiou só o ID do projeto, esse é o cenário clássico. O plugin WP inseria o snippet completo; a SPA nova herdou apenas metade da instalação.
Agências que entregam landing em Elementor ou Webflow e depois replatformam para Next.js veem o mesmo padrão: marketing exporta “código Clarity”, engenharia cola só a URL do script no useEffect, stakeholders assinam go-live com Network verde e dashboard morto. Retrabalho custa reputação — melhor incluir typeof no checklist de aceite.
Migração de WordPress, GTM ou HTML estático para React
Se você herda Clarity de um site anterior, comece pelo diff de instalação — não pelo Project ID.
WordPress plugins e injetores de tag colam o snippet completo no head. Google Tag Manager dispara HTML customizado com stub e tag juntos. HTML estático segue o manual Microsoft linha a linha. React SPA quebra quando alguém traduz isso para “carregar script no mount” sem ler a primeira metade do snippet.
Ao migrar, exporte o HTML fonte do site antigo e compare com seu AnalyticsProvider. Se faltar a função clarityStub, você encontrou a regressão antes do deploy — não depois de três sprints de growth.
GTM merece atenção extra: containers que disparam apenas Custom HTML com src do tag, sem stub inline, reproduzem o bug em SPA. Soluções: mover Clarity para provider React com controle total, ou garantir que o HTML do GTM inclua as duas partes na ordem correta. Muitos times desligam Clarity do GTM após migrar para React justamente para eliminar essa classe de race.
Handoff entre marketing e engenharia deve incluir link para documentação manual da Microsoft, não só o Project ID copiado do painel. ID identifica o destino dos dados; não substitui instrução de implementação.
Para sites híbridos — blog WordPress + app React no mesmo domínio — use projetos Clarity separados ou filtros por URL no painel. O importante é cada stack respeitar stub onde injeta tag. App React quebrado não se conserta herdando gravações do blog WP.
Depois da migração, agende revisão de analytics em trinta dias: onboarding destravado, Recordings com volume coerente com GA4, tags customizadas aparecendo nos filtros. Esse ritual evita que o bug volte silenciosamente no próximo refactor de provider.
Por que Clarity quebra em SPAs React
A documentação da Microsoft mostra um snippet em duas partes. A primeira cria uma fila no objeto global:
window.clarity = window.clarity || function () {
(window.clarity.q = window.clarity.q || []).push(arguments)
}
A segunda injeta o script remoto:
<script async src="https://www.clarity.ms/tag/SEU_PROJECT_ID"></script>
O JavaScript servido em clarity.ms/tag/*.js chama window.clarity(...) imediatamente ao carregar. Se o stub ainda não existir, a execução falha sem erro visível no console — e nenhum beacon de gravação é enviado.
Esse comportamento é diferente do Google Analytics 4 com gtag: o GA4 também usa uma fila (dataLayer) antes do script remoto, e bibliotecas maduras como PostHog fazem init interno. Clarity não tolera script solto — o contrato é explícito no manual de instalação da Microsoft.
O anti-padrão mais comum
Em React, equipes costumam fazer algo assim no useEffect:
useEffect(() => {
const script = document.createElement('script')
script.src = `https://www.clarity.ms/tag/${projectId}`
document.head.appendChild(script)
}, [])
Isso reproduz exatamente o problema: o src aparece no Network, mas window.clarity nunca foi definido antes.
Outras variantes que falham do mesmo jeito:
- Copiar só a linha
<script src="…">paraindex.htmldepois que o React já montou e removeu nós do head. - Usar
next/scriptcomstrategy="lazyOnload"sem stub prévio — o lazy load piora a corrida: o remoto pode executar antes de qualquer init manual. - Carregar Clarity via GTM sem tag HTML customizada com stub — o container injeta o script remoto sozinho.
Sites estáticos que colam o snippet completo no <head> raramente veem esse bug. SPAs que injetam só o script remoto são o caso típico — especialmente em Vite + React, Create React App e Next.js App Router com analytics no client component.
Documentação de migração frequentemente lista apenas o Project ID e um exemplo de script tag — omitindo o stub porque assume WordPress ou HTML estático. Times full-stack React seguem a doc literalmente e herdam o bug. A Microsoft atualizou o manual de instalação manual, mas tutoriais de terceiros ainda circulam com metade do snippet. Sempre valide contra a fonte oficial antes de fechar ticket de analytics.
Por que o erro é silencioso
Ferramentas de QA olham para:
- Presença do script no DOM ✓
- Status 200 no Network ✓
- Variável de ambiente configurada ✓
Nenhum desses checks prova que window.clarity existia no momento em que o remoto executou. Sem monitoramento ativo (Console ou collect beacons), o time só descobre semanas depois, quando alguém abre o dashboard para analisar um funil de conversão e encontra zero gravações.
Em retrospectiva, o padrão se repete: deploy de feature nova, analytics “já estava configurado”, ninguém revalida o tag. A dívida aparece na primeira reunião de CRO ou growth, quando pedem evidência visual do comportamento do usuário. Corrigir leva minutos; recuperar semanas de dados perdidos, não.
Clarity vs GA4 e PostHog: papéis diferentes na mesma SPA
Times maduros raramente dependem de uma única ferramenta. GA4 responde volume e aquisição. PostHog responde funis, feature flags e eventos customizados com identidade. Clarity responde comportamento visual — cliques, scroll, rage clicks, gravações de sessão.
Cada uma exige init correto no client. GA4 popularizou o padrão dataLayer + gtag shim. PostHog encapsula init na biblioteca npm. Clarity exige stub explícito — não há atalho equivalente ao “só importar o pacote” se você optar pelo tag manual.
Quando Clarity está quebrado mas GA4 funciona, a empresa continua vendo pageviews — e assume que “analytics está ok”. Na verdade, metade da stack está cega para UX. Priorize validar Clarity no mesmo checklist de deploy que valida conversões e erros JavaScript.
Privacidade também difere: Clarity mascara inputs de formulário por padrão, mas grava interações de DOM. Alinhe política de cookies e banner de consentimento antes de ligar gravação em mercados com LGPD rigorosa. O stub não resolve compliance — só faz a gravação funcionar quando você já decidiu usar a ferramenta.
Em auditorias de stack de marketing, listamos Clarity ao lado de GA4 e ferramentas de experimento. Quando o cliente reporta “Clarity nunca funcionou”, em oitenta por cento dos casos SPA React a causa é stub ausente — não bloqueio de cookie, não Project ID errado. Isso economiza horas de investigação em outras camadas.
Manter um doc interno com o init canonical e o comando de smoke reduz regressões quando estagiários ou fornecedores tocam no AnalyticsProvider. Git blame no arquivo de init costuma revelar PR que “simplificou” o código removendo o stub.
Diagnóstico em dois minutos
Abra o site em produção (ou preview) e rode no Console:
typeof window.clarity
- Resultado
"function"SignificadoBootstrap OK — se ainda não há sessões, verifique ad blocker ou aguarde lag de ingestão - Resultado
"undefined"SignificadoStub ausente — gravação não funciona, mesmo com tag no Network
Confirme também se há requests para a.clarity.ms/collect após navegar por algumas páginas. Sem collect, o dashboard não recebe dados.
Passo a passo no DevTools
- Aba Console —
typeof window.claritye, se function,window.clarity.q(fila pendente antes do remoto carregar). - Aba Network — filtro
clarity. Espere vertag/{id}(200) e, após interação,collect(204 ou 200). - Aba Application → Frames — em sites com iframe de consentimento, confirme que o tag roda no frame principal, não só no banner.
- Modo anônimo — extensões de privacidade costumam bloquear collect mesmo com stub correto.
- Comparar preview vs produção — env ausente em preview gera falso negativo entre ambientes.
Documente o resultado do smoke em ticket ou runbook. Um print do Console com typeof function vira evidência quando marketing questiona dados vazios e engenharia precisa provar quando o fix entrou.
Se typeof retorna function mas collect não aparece, investigue consent mode, CSP ou adblock — este artigo cobre stub ausente, o caso mais frequente em React.
Se você usa React Strict Mode em desenvolvimento, o useEffect pode rodar duas vezes. Por isso o init deve ser idempotente: checar se window.clarity já é function e se o script tag já existe antes de reinserir.
Em produção, outro cenário comum é hydration mismatch: o server renderiza HTML sem Clarity, o client monta e injeta o tag — correto — mas algum error boundary ou suspense boundary remonta o provider e tenta reinserir script duplicado. Dedupe evita dois tags competindo; idempotência evita race entre eles.
Teste também em mobile Safari e Chrome Android. Gravações funcionam, mas ad blockers em browsers alternativos variam. Se seu ICP usa mobile corporativo com perfis gerenciados, inclua esse dispositivo no smoke test — não só desktop de desenvolvedor.
Casos reais: o que muda depois do fix
Depois de corrigir o stub em produção, três mudanças aparecem rapidamente para o time.
Onboarding destrava. O painel Clarity sai de “Quase lá” assim que a primeira sessão válida chega. Não é necessário reconfigurar o projeto — só dados reais.
Recordings substituem suposições. Product managers param de debater “será que o usuário viu o CTA?” e passam a ver scroll e cliques. Em formulários longos, rage clicks indicam fricção antes da queda no funil quantitativo.
Heatmaps acumulam em rotas SPA. Como a URL muda sem reload completo, Clarity associa interações à rota client-side — desde que o tag esteja ativo durante a navegação. Rotas lazy-loaded precisam manter o provider de analytics acima do router, não dentro de cada page chunk.
Empresas B2B com ciclo longo se beneficiam disso em landing pages e ferramentas de diagnóstico: a visita anônima antes do lead fica visível. Você vê onde o visitante hesita antes de preencher e-mail — insight que GA4 sozinho raramente entrega com a mesma riqueza visual.
O fix técnico é pequeno; o impacto em decisão de produto é desproporcional. Por isso vale documentar no repositório: link para este padrão de init, smoke test no README de deploy, e menção no PR template quando analytics init for alterado.
Para squads que já usam como implementar IA na empresa como north star de maturidade, Clarity fecha o loop de evidência: você implementa IA nos processos e mede se o usuário realmente completa o fluxo digital que sustenta esse processo. Sem gravação, você otimiza modelo e automação no escuro.
Consultores e agências que entregam site React para clientes devem incluir validação de Clarity no handoff — junto com Lighthouse e formulário de contato. Cliente não técnico só descobre o bug meses depois, quando contrata growth e percebe histórico vazio. Handoff profissional inclui screenshot do Console com typeof function e link para primeira gravação.
Correção: stub + tag na ordem certa
O padrão que funciona em React/Vite espelha o snippet oficial. Primeiro o stub, depois o script:
export function initClarity() {
const projectId = import.meta.env.VITE_CLARITY_PROJECT_ID?.trim()
if (!projectId || typeof window === 'undefined') return
if (typeof window.clarity === 'function') return
if (document.querySelector('script[src*="clarity.ms/tag"]')) return
window.clarity =
window.clarity ||
function (...args) {
(window.clarity.q = window.clarity.q || []).push(args)
}
const script = document.createElement('script')
script.async = true
script.src = `https://www.clarity.ms/tag/${projectId}`
document.head.appendChild(script)
}
Chame initClarity() uma vez no mount do provider de analytics — junto com GA4 e PostHog, se usar — e dedupe com checagens de stub e script já presentes.
Onde chamar no ciclo de vida React
Padrão recomendado:
// AnalyticsProvider.tsx
useEffect(() => {
initGoogleAnalytics()
initPostHog()
initClarity()
}, [])
Evite chamar init dentro de rotas filhas — cada remount duplica lógica e complica dedupe. Um provider no root do app centraliza analytics e respeita a ordem stub → tag para todos os tags.
Em Next.js App Router, coloque o provider em layout.tsx como Client Component ('use client'). Se usar next/script, injete o stub inline antes do componente Script:
<Script id="clarity-stub" strategy="beforeInteractive">
{`window.clarity=window.clarity||function(){(window.clarity.q=window.clarity.q||[]).push(arguments)}`}
</Script>
<Script src={`https://www.clarity.ms/tag/${projectId}`} strategy="afterInteractive" />
Alternativa oficial: pacote @microsoft/clarity, que encapsula o bootstrap. Em projetos com vários tags de analytics, o stub manual costuma ser mais leve e previsível.
Variável de ambiente correta
Use VITE_CLARITY_PROJECT_ID (ou equivalente do seu bundler) com o ID alfanumérico curto do projeto — não confunda com tokens de API.
Em deploys Vite/Cloudflare Pages, a variável precisa existir no momento do build (VITE_* é inlined no bundle). Secret no painel sem rebuild local não basta quando o CI builda fora do servidor.
Após deploy, valide no bundle de produção: busque o project ID ou domínio clarity.ms/tag no JS servido — confirma que a variável foi inlined, não que ficou vazia silenciosamente.
Em monorepos com preview por branch, cada preview precisa do mesmo Project ID ou projetos separados no painel Clarity. Misturar tráfego de staging e produção no mesmo ID polui gravações — mas ainda exige stub funcional em ambos os ambientes.
Referência oficial
A Microsoft documenta o snippet completo em Setup → Install manually no painel Clarity. Sempre compare seu init React com esse snippet — linha por linha — antes de abrir issue no repositório ou no suporte. Na maioria dos casos, a diferença é só a ordem stub → tag.
Project ID vs token de API: não misture
Dois credenciais diferentes aparecem na documentação — e trocá-las gera confusão:
- Project IDTag no site (
clarity.ms/tag/…) — String curta (ex.:x1f9gr3ysf) - Data Export JWTMCP, API de exportação, integrações agente — Token longo em Settings → Data Export
O site precisa do Project ID. Ferramentas de agente (MCP do Clarity, scripts de exportação) usam o JWT — credencial separada, limite de requisições por dia, sem relação com a gravação no browser.
Se o MCP mostra zero sessões mas o collect funciona no Network, pode ser lag da API de exportação — não necessariamente bug no site.
Erros comuns ao configurar MCP ou API
Desenvolvedores que configuram o Clarity MCP para agentes de IA às vezes usam o Project ID no lugar do JWT Data Export. O MCP responde com erro de autenticação ou lista vazia. Outros invertem: colocam o JWT no front-end — o que expõe credencial de back-office no bundle público.
Regra prática: nada de JWT no client. Project ID no site; JWT apenas em variáveis de ambiente local do operador ou CI secreto de jobs de exportação — nunca em variáveis expostas ao browser.
Integrações com agentes de IA (MCP, scripts de exportação) cresceram em 2025–2026, e a confusão entre credenciais piorou. Documente no runbook qual chave vai onde — Project ID na tabela de build, JWT na tabela de agent tooling. Futuro você agradece quando outro dev não expuser token no pull request.
O que perguntar antes de cada deploy com Clarity
Checklist de três perguntas para tech lead ou revisão de PR:
O stub roda antes do tag em todos os caminhos de entrada? Preview, produção, branch deploy — mesmo código de init. Se um ambiente usa GTM e outro usa provider React, cada caminho precisa do stub.
O init é idempotente? Strict Mode, navegação client-side e hot reload não devem duplicar scripts ou quebrar a fila. Checagens de stub existente e script já inserido são obrigatórias.
Existe smoke test pós-deploy? Mesmo manual: alguém abre produção, roda typeof no Console, confirma collect. Automatizar depois; ritualizar agora.
Times que tratam Clarity como “config de marketing” sofrem mais. Quem trata como dependência de runtime — igual Sentry ou auth — mantém gravações estáveis entre releases.
Tags customizadas: filtrar gravações por funil
Depois que o stub funciona, você pode enriquecer sessões com metadados de produto. A API clarity('set', chave, valor) associa tags à gravação atual — útil em SPAs onde a URL sozinha não conta a história (wizard multi-step, modais, estados sem mudança de rota).
Exemplos práticos:
tool_slug— identifica qual diagnóstico ou calculadora o usuário abriu.lead_stage—anonymousantes do formulário,capturedapós submit.score_band— faixa de resultado em quizzes de maturidade.
No React, chame claritySet após eventos de negócio — mount da ferramenta, conclusão de etapa, captura de lead — não a cada render. Tags enviadas cedo demais poluem; enviadas tarde demais perdem a sessão inicial.
Heatmaps passam a refletir não só páginas, mas contexto de produto. Isso acelera decisões de UX: em qual pergunta do quiz a maioria desiste? O formulário de lead some da viewport em mobile?
Combine com funis quantitativos no PostHog ou GA4. Clarity responde como o usuário interagiu; funis respondem quantos chegaram a cada etapa. Os dois juntos fecham o loop de otimização — especialmente em ferramentas de diagnóstico operacional onde cada etapa tem peso comercial.
Checklist pós-fix
Depois de deployar o stub correto:
- Visite o site em Chrome normal, sem ad blocker.
- Navegue por 2–3 rotas (em SPA, troque de página para gerar eventos).
- Console:
typeof window.clarity === 'function'. - Network: confirme
a.clarity.ms/collect. - Dashboard → Recordings — aguarde ~5 minutos na primeira sessão pós-fix.
- Opcional: defina uma tag customizada e confirme no painel de filtros do Clarity.
Smoke test automatizável
Para CI ou checklist de deploy, considere um script Playwright ou check manual documentado:
- Carregar URL de produção.
- Assert
typeof window.clarity === 'function'. - Assert existência de request
clarity.ms/collectapós click em CTA.
Não substitui visita humana para validar Recordings — mas pega regressões de stub antes de merge.
Quando ainda não aparece gravação
Se stub e collect estão OK mas Recordings demora:
- Tráfego de bots filtrado pelo Clarity — visita real em browser completo.
- Consent banner bloqueando tags até aceite — teste após aceitar cookies.
- Projeto errado no dashboard — confira Project ID no painel vs variável de build.
- Lag de ingestão na primeira sessão — aguarde até 15 min em projetos novos.
Se você mede conversão em ferramentas interativas, vale cruzar heatmaps do Clarity com funis em PostHog ou GA4. O artigo como automatizar tarefas repetitivas com IA mostra por que métricas de funil importam quando o objetivo é reduzir trabalho manual.
Como a Harpia mede funis sem perder sessões
Sites que a Harpia constrói via integração de dados e ferramentas combinam analytics de produto (PostHog), comportamento visual (Clarity) e monitoramento de erros (Sentry). Cada camada responde a uma pergunta diferente: onde o usuário abandona, o que ele clicou e se algo quebrou no caminho.
Esse stack só funciona quando cada tag inicializa corretamente no mount da SPA — sem depender de HTML estático que React substitui. Por isso validamos window.clarity, beacons de collect e funis de ferramenta antes de fechar deploy.
Em ferramentas interativas — diagnósticos, quizzes, calculadoras — tags customizadas como tool_slug e lead_stage permitem filtrar gravações por etapa do funil. Sem Clarity funcionando, o time perde a camada visual justamente onde o drop-off mais importa: formulários multi-step e telas de resultado.
A lição do stub não é só sobre Microsoft Clarity. Qualquer script third-party que assume um global pré-existente exige o mesmo cuidado em SPA. Tratar analytics como código de produção — com init idempotente, dedupe e smoke test pós-deploy — evita semanas de dados perdidos.
Para entender como analytics se conecta a processos operacionais mais amplos, veja como aplicar IA em processos reais da empresa. Para exemplos de automação que dependem de funis medidos, veja também exemplos de automação com IA. A solução IA para marketing organiza as camadas de analytics, conteúdo e performance. O playbook de e-commerce conecta medição de funis e otimização de conversão.
Conclusão
Microsoft Clarity em React não falha porque o Project ID está errado — falha, na maioria das vezes, porque o stub window.clarity não existe antes do script remoto. O dashboard fica em "Quase lá", o Network parece saudável e ninguém percebe até alguém abrir o Console.
O fix leva minutos: stub oficial, tag depois, init no provider, visita real para validar. Depois disso, heatmaps e gravações passam a refletir o comportamento real dos usuários — inclusive em rotas dinâmicas de SPA. Tags customizadas e funis quantitativos completam o quadro para times que otimizam conversão em ferramentas e fluxos de lead.
Trate analytics em SPA como código de produção: idempotente, testável, documentado no runbook de deploy. O custo de uma regressão silenciosa é sempre maior do que cinco minutos de DevTools após cada release.
Se você quer mapear onde usuários abandonam funis de diagnóstico ou ferramentas no seu site, solicite um diagnóstico com a Harpia.
