Lazy-load seu wallet SDK para corrigir INP e passar Core Web Vitals
Os SDKs de wallet-connect são o maior assassino de INP em sites Web3. Carregar esses SDKs de forma eager em cada página custa 200-400KB de JS e 200-400ms de TBT. Veja como fazer o lazy-load deles em 30 minutos.
Por que isso importa
- →Wallet SDK eager-load is the most common Web3 CWV killer across 200+ TG3 audits.
- →Google's 2026 INP target is under 200ms. Eager-loaded RainbowKit alone adds 250-380ms TBT on mid-range mobile.
- →Magic Square (cliente de TG3) went from 380ms INP to under 200ms in 14 days using exactly this pattern.
- →1.6x organic traffic lift in 60 days from CWV improvements alone on Magic Square. The fix is 10 lines of code.
Estado anterior (como o ruim se parece)
// pages/_app.tsx (Next.js example)
import { WagmiConfig, createConfig } from 'wagmi';
import { ConnectKitProvider, getDefaultConfig } from 'connectkit';
// SDK loads on every page mount, even marketing pages
const config = createConfig(getDefaultConfig({
appName: 'My dApp',
walletConnectProjectId: 'xxx',
}));
export default function App({ Component, pageProps }) {
return (
<WagmiConfig config={config}>
<ConnectKitProvider>
<Component {...pageProps} />
</ConnectKitProvider>
</WagmiConfig>
);
}
Paso a paso
Passo 1: Meça seu INP base
Abra o Chrome DevTools, vá para a aba Performance, faça throttle para "Slow 4G" e 4x CPU slowdown (simula Android de gama média). Recarregue sua homepage e capture um trace de Performance. Anote o Total Blocking Time (TBT) e Interaction to Next Paint (INP). Qualquer coisa acima de 200ms INP falha o CWV.
Passo 2: Identifique o wallet SDK no seu bundle
Rode um bundle analyzer. Para Next.js: ANALYZE=true npm run build. Para Vite: vite-bundle-visualizer. Busque @rainbow-me/rainbowkit, @web3modal/wagmi, wagmi, ethers, viem. Esses tipicamente representam 200-500KB de JS em sites Web3. Esse é seu alvo para lazy-loading.
# Next.js bundle analyzer
npm i -D @next/bundle-analyzer
# Then add to next.config.js and run:
ANALYZE=true npm run build
Passo 3: Marque o botão de connect com um atributo data
Adicione data-connect-wallet a cada botão <Connect Wallet> através do seu site. Esse é o trigger que carrega o SDK. Não use handlers onClick porque precisamos detectar o click antes que o handler rode (o handler ainda não existe nesse ponto).
<button data-connect-wallet className="...">Connect Wallet</button>
Passo 4: Implemente o wrapper de dynamic import
Use o dynamic() do Next.js com ssr:false para fazer lazy-load do wallet provider. O componente wrapper renderiza children diretamente até que um botão connect seja clicado, depois carrega o SDK e envolve os children no provider real. Use o código do "Estado posterior" como seu template.
Passo 5: Mova a inicialização do SDK para o componente interno
Todo o código de config de wagmi/RainbowKit/Web3Modal vai no componente interno (carregado sob demanda). O wrapper externo apenas escuta o trigger. Dessa forma, o código de config do SDK só roda quando é necessário.
Passo 6: Teste o fluxo de connect end-to-end
Clique em "Connect Wallet" na sua homepage. Verifique: (1) o SDK carrega (a aba Network mostra os chunks), (2) o modal abre, (3) a conexão de wallet completa, (4) as ações pós-connect funcionam normalmente. A experiência para o usuário deve ser idêntica a antes.
Passo 7: Meça o INP de novo e confirme a melhoria
Rode de novo o trace de Performance do Chrome DevTools do Passo 1. INP deve cair significativamente (típico: 380ms → 180ms). Rode PageSpeed Insights para a versão de field-data (a data de CrUX leva 28 dias para atualizar completamente, porém a data de lab atualiza imediatamente). Documente o antes/depois para sua equipe.
FREE WEB3 AUDIT
Veja onde esse playbook se aplica no seu site.
Rode uma auditoria Crawlux grátis antes de começar o playbook. Diz quais correções são mais urgentes.
Primeira auditoria grátis · Sem cadastro · 60 segundos · Full PDF report
Estado posterior (como o bom se parece)
// components/WalletProvider.tsx - dynamic import
import dynamic from 'next/dynamic';
import { useState } from 'react';
const WalletProviderInner = dynamic(
() => import('./WalletProviderInner'),
{ ssr: false, loading: () => null }
);
export function WalletProvider({ children }) {
const [walletNeeded, setWalletNeeded] = useState(false);
// Listen for the connect button click
if (!walletNeeded) {
return (
<div onClick={(e) => {
if (e.target.closest('[data-connect-wallet]')) {
setWalletNeeded(true);
}
}}>
{children}
</div>
);
}
return <WalletProviderInner>{children}</WalletProviderInner>;
}
// components/WalletProviderInner.tsx - actual SDK
import { WagmiConfig, createConfig } from 'wagmi';
import { ConnectKitProvider, getDefaultConfig } from 'connectkit';
const config = createConfig(getDefaultConfig({
appName: 'My dApp',
walletConnectProjectId: 'xxx',
}));
export default function WalletProviderInner({ children }) {
return (
<WagmiConfig config={config}>
<ConnectKitProvider>{children}</ConnectKitProvider>
</WagmiConfig>
);
}
Como validar a correção
- ✓PageSpeed Insights lab data: INP under 200ms.
- ✓Bundle analyzer: wallet SDK chunks not present in initial JS payload (should be in async chunks).
- ✓Network tab on first page load: zero requests to wallet SDK files.
- ✓Network tab after clicking connect: SDK chunks load, modal opens within 500ms.
- ✓After 28 days: PageSpeed Insights field data (CrUX) shows INP improvement at the 75th percentile.
Erros comuns
Pitfall
Esquecer a dependência de contexto do wagmi
Alguns componentes rio abaixo podem usar useAccount() do wagmi esperando que o provider exista. Envolva esses com renderização condicional ou use um valor padrão quando o SDK ainda não estiver carregado.
Pitfall
Erros SSR de dynamic import
Sempre use { ssr: false } nas opções de dynamic(). Os SDKs de wallet assumem globais do navegador (window, localStorage) e caem durante SSR.
Pitfall
Auto-connect en reload de página no funciona
Se os usuários tinham auto-connect habilitado, esperam ser auto-conectados ao recarregar. Solução: verifique a cookie/localStorage de conexão persistida no mount inicial e dispare setWalletNeeded(true) se for encontrado. Caso contrário auto-connect quebra.
Pitfall
Botão connect em uma rota diferente disparando carga cedo demais
Se seu site tem o botão connect em um header global e um usuário chega em /blog/some-post/, o SDK carrega ao click, porém o usuário já está profundo no conteúdo. Isso está bem na verdade; o objetivo é evitar carregar no mount inicial de página, não nunca carregar.
Pitfall
Problemas de tree-shaking com providers do wagmi
wagmi 2.x tem sido mais agressivo com tree-shaking. Alguns imports disparam carga completa do SDK. Use o bundle analyzer para verificar que seu chunk dinâmico não inclua acidentalmente o SDK inteiro no main bundle.
Si algo se rompe: rollback
Reverta WalletProvider.tsx para a versão eager-load. A wallet funciona exatamente como antes em minutos. INP volta à linha base. Considere esse rollback apenas se o padrão dynamic-load quebra fluxos de usuário; a melhoria de performance vale esforço significativo para corrigir para frente.
Rode uma auditoria Crawlux grátis sobre essa correção
Crawlux valida as correções de schema, técnicas e AEO desse playbook automaticamente. Plano grátis em um domínio.
Executar auditoria gratuita →FAQ
Isso funciona com Vite ou Vue ou outros frameworks?
Sim. O padrão é universal: dynamic import atrás de um trigger de ação de usuário. Vite tem React.lazy() com Suspense. Vue tem defineAsyncComponent. Mesma ideia: só carrega o wallet SDK sob demanda, não no mount inicial.
E se eu preciso de conexão de wallet no início do app (ex., dashboard SaaS)?
Então o lazy-loading não é o padrão correto; eager-load é correto para rotas de app. Aplique esse padrão apenas a páginas de marketing e páginas de conteúdo onde a conexão de wallet é opcional. Use carga baseada em rota: as rotas de marketing lazy-load, as rotas de app eager-load.
Isso vai quebrar os deep links do WalletConnect?
Não. As URIs do WalletConnect são geradas quando o modal abre. Enquanto o SDK carregar quando o modal precisa renderizar, os deep links funcionam normalmente.
Como eu manejo o provider injetado do metamask?
O window.ethereum injetado do MetaMask está disponível sem o SDK. Se você quer detectar a presença do MetaMask na carga inicial (sem carregar o SDK completo), verifique window.ethereum?.isMetaMask. Faça lazy-load do SDK apenas quando o usuário clica em connect.
Isso afeta o SEO?
Positivamente. As páginas mais rápidas rankeiam melhor. INP é um sinal de ranking confirmado no algoritmo do Google de 2026. O SDK não é conteúdo SEO-relevante (é código funcional), então removê-lo da carga inicial não danifica nada que o Google indexe.
Playbooks relacionados
Guias pilares
Módulos de auditoria
RUN YOUR FIRST AUDIT
Rode o playbook contra uma auditoria real.
Receba um relatório de auditoria Crawlux grátis e use-o como linha base para o trabalho nesse playbook.
Primeira auditoria grátis · Sem cadastro · 60 segundos · Full PDF report
