1

Minha landing tinha 386 KB de SPA e comia 2 de cada 3 cliques pagos. Achei hoje de madrugada

Contexto: construi o Dosy, um app Android que organiza a medicacao de uma FAMILIA inteira numa conta so. Nao e mais um lembrete de remedio individual sao varios pacientes (mae idosa, filho em tratamento, voce, e ate o pet), cada um com a lista dele, e o dia de todos numa tela. Alarme nativo em tela cheia que toca no silencioso, historico de adesao, relatorio PDF pro medico, e um segundo cuidador que acompanha e marca dose junto.

Ontem subi 4 anuncios no Meta pra achar testadores. Resultado do dia:

  • 8.720 pessoas alcancadas
    • 458 cliques, CTR de 5,4% (umas 2x a media da categoria)
    • CPC de R$ 0,21
    • 218 visualizacoes de pagina
    • zero cadastros

Gastei um tempo achando que era copy. Nao era.

O que os numeros do GA4 diziam, e que eu demorei pra ler direito: 263 usuarios dispararam page_view e so 86 dispararam o evento proprio da landing. Os dois eventos estao no mesmo arquivo. A diferenca e QUANDO cada um roda: o page_view sai assim que o gtag inicializa, e o outro so quando o componente React da pagina monta.

Ou seja: 177 pessoas carregaram o suficiente pro Analytics existir, e foram embora antes da pagina existir.

Fui medir o que a landing realmente pedia pro navegador antes do primeiro pixel:

index .............. 121,4 KB
react-vendor ....... 101,9 KB
dosy ...............  78,3 KB
supabase ...........  51,0 KB
tanstack ...........  20,2 KB
css ................   6,5 KB
+ runtime e outros
TOTAL .............. 386,6 KB comprimidos (~1,5 MB descomprimidos), 10 arquivos

Mais um chunk lazy da propria pagina. Mais um @import de Google Fonts DENTRO do CSS que e a pior forma possivel de carregar fonte, porque o navegador so descobre o pedido depois de baixar o CSS e ai serializa mais duas idas ao servidor.

E o <body> do HTML servido era literalmente isso:

<body><div id="root"></div></body>

2 KB de HTML sem uma palavra dentro.

O erro de raiz nao foi performance. Foi arquitetural, e obvio depois: eu tratei uma pagina de captura como se fosse uma rota da aplicacao. Ela morava dentro do SPA porque era comodo reaproveitar componente e estilo. So que trafego pago do Feed do Facebook abre no WEBVIEW do app, num Android de entrada, em 4G. Nesse ambiente, 386 KB e varios segundos de tela branca e ninguem espera tela branca de um link de anuncio.

O tempo de engajamento somado confirmava, e eu tinha esse dado na mao o tempo todo:

/quemdeu  48 sessoes -> 70 segundos NO TOTAL
/juntos   32 sessoes ->  0 segundos
/lembrete 31 sessoes ->  1 segundo
/descanso 23 sessoes ->  0 segundos

Refiz as landings como HTML estatico gerado do mesmo arquivo de conteudo que alimentava as versoes React (uma fonte da verdade so). CSS inline, fonte do sistema, e JavaScript apenas pra medir e enviar o formulario nunca pra desenhar.

386,6 KB -> 6,0 KB na rede. TTFB de ~240 ms.

Tres coisas que aprendi e que valem mais que o numero:

  1. Pagina que recebe trafego pago nao e rota de aplicacao, e documento. Se o conteudo depende de download pra existir, voce esta pagando por clique de gente que nunca vai ver o conteudo.

  2. Instrumente dois eventos em momentos diferentes do boot. Foi so o vao entre page_view e o evento do componente que revelou o problema. Um evento so teria mostrado "chegou pouca gente" e eu ia culpar o anuncio pelo resto da semana.

  3. Cuidado com o que o build assume do ambiente. Na primeira geracao das paginas estaticas, o formulario saiu apontando pro Supabase de LOOPBACK, porque o .env.local da minha maquina aponta o VITE_SUPABASE_URL pro banco local do npm run dev. Ia subir bonita, aceitar o e-mail e o POST morreria em 127.0.0.1 no celular de quem clicou. Zero erro no build, zero cadastro, zero pista. Coloquei um guard que quebra o build se a URL escolhida for loopback.

No mesmo dia achei tambem um honeypot que devolvia {ok:true} e ia embora sem gravar nada. Pra bot, perfeito. Pra uma pessoa cujo gerenciador de senha preencheu o campo escondido, a tela dizia "Pronto" e o cadastro simplesmente nao existia. Agora grava marcado, e so o e-mail e pulado.


A parte do pedido, e vou ser direto porque acho que voces preferem assim: o app esta em teste fechado no Google Play e eu preciso de 12 pessoas usando por 14 dias e a exigencia e de 14 dias CONTINUOS, entao quem sai no meio rebobina o relogio.

Se voce cuida do remedio de alguem em casa (pai, mae, filho, ou voce mesmo com contnuo), e o publico exato pra quem isso foi feito, e eu quero MUITO a reclamacao de voces inclusive "essa tela ta feia" e "nao entendi esse botao".

Stack, pra quem tiver curiosidade: React 19 + Vite + Capacitor, Supabase atras, e o alarme em Java nativo porque WebView nao da conta de alarme confiavel em Doze mode.

Critica de arquitetura e de UX e bem-vinda. Se tiver furo, prefiro descobrir agora.

Carregando publicação patrocinada...
4
1

Juro que nao fui eu !!! Eu nao uso acentos, mas eh uma escolha estilistica minha com mais de 30 anos (por conta das BBS/usenet), mas por exemplo uso "eh" para referenciar "é" e o autor nem isso fez.

2

É engraçado que eu precisei colocar no CLAUDE.md do pc inteiro pra ele usar acentos em ptbr, porque ele escrevia todos os documentos em ascii sem qualquer acentuação uaheuahehuaehuae

mas usar "eh" entrega um pouquinho a idade ein auehueahhuae

0