O problema
As ferramentas de teste A/B do lado do cliente funcionam carregando um script que modifica o DOM depois que o navegador já começou a construir a página. Sem intervenção, o usuário vê brevemente o conteúdo original antes que ele pisque ou mude para a variante do experimento. As plataformas de teste sempre contornaram esse problema com "snippets antioscilação" que ocultam a página inteira com opacity: 0 até que o script do experimento termine ou um tempo limite arbitrário (normalmente 4 segundos) se esgote. Essa abordagem sacrifica a renderização progressiva, permite cliques acidentais em conteúdo invisível e introduz ciclos de pintura desnecessários.
A solução
O atributo blocking=render permite que um elemento <script> ou <link> colocado no <head> bloqueie a renderização, mas não a análise, até que o recurso seja buscado e executado. Isso dá aos scripts de experimento o mesmo comportamento de bloqueio de renderização que as folhas de estilo têm por padrão, garantindo que o navegador nunca mostre a página até que a variante do experimento tenha sido aplicada. Sem hacks de opacidade, sem tempos limite arbitrários e sem oscilação.
Estratégia de implementação
- OBRIGATÓRIO: coloque o script de experimento no documento
<head>e adicioneblocking="render". - OBRIGATÓRIO: verifique se o script tem
type="module"(preferencial para scripts inline) ouasync(preferencial para scripts externos com um valorsrc). - Mantenha o script de experimentação pequeno e rápido. Como a renderização é bloqueada até que o script seja executado, um script grande ou lento atrasa diretamente a primeira exibição.
- NÃO use
blocking="render"em scripts que não precisam ser executados antes da primeira exibição. Ele é destinado apenas a scripts cuja saída precisa estar visível na renderização inicial. - NÃO aplique
blocking="render"a scripts fora do<head>. Somente os scripts no<head>podem bloquear a renderização.
Guia de implementação
Configuração básica
OBRIGATÓRIO: carregue o script de experimento com async e blocking="render". O atributo async garante que o script não bloqueie a análise de HTML (o navegador continua criando o DOM enquanto busca o script). O atributo blocking="render" garante que o navegador não mostre nada até que o script seja executado.
<head>
<!--
MANDATORY: Both `async` and `blocking="render"` are required.
- `async`: Prevents parser-blocking, so the DOM is built in parallel.
- `blocking="render"`: Holds rendering until the script executes,
ensuring experiment changes are applied before the user sees anything.
-->
<script
src="https://cdn.example.com/experiment-sdk.js"
async
blocking="render"
></script>
</head>
Carregando estilos específicos do experimento
Se o experimento exigir uma folha de estilo de variante, use blocking="render" no elemento <link>. As folhas de estilo no <head> já bloqueiam a renderização por padrão, mas as folhas de estilo injetadas dinamicamente ou adicionadas por script não. Use blocking="render" explicitamente quando a folha de estilo for adicionada de forma dinâmica ou quando você quiser deixar clara a intenção.
<head>
<!--
DO: Use blocking="render" on experiment stylesheets that are
dynamically inserted or conditionally loaded by the experiment SDK.
This ensures variant styles are applied before first paint.
-->
<link
rel="stylesheet"
href="https://cdn.example.com/experiment-variant-b.css"
blocking="render"
>
</head>
Script de experimento inline
Se a lógica do experimento for leve o suficiente para ser inline, use um script de módulo inline com blocking="render". Isso é útil quando a lógica do experimento busca uma configuração e aplica mudanças no DOM diretamente.
<head>
<!--
DO: Use an inline module script when the experiment logic is small.
Module scripts are deferred by default (non-parser-blocking),
and blocking="render" ensures rendering waits for execution.
-->
<script type="module" blocking="render">
// Fetch the experiment configuration from your testing platform.
const config = await fetch('/api/experiment?id=homepage-cta')
.then(res => res.json());
// Apply the variant by setting a data attribute on <html>.
// CSS rules keyed to this attribute will style the variant.
document.documentElement.dataset.variant = config.variant;
</script>
<style>
/* Default styles (control group) */
.cta-button {
background-color: blue;
}
/* Variant B styles, activated by the data attribute */
[data-variant="b"] .cta-button {
background-color: green;
}
</style>
</head>
Práticas recomendadas
- OBRIGATÓRIO: o script de experimentação PRECISA ser executado rapidamente. Um script lento atrasa toda a renderização. Defina um montante de desempenho (por exemplo, tempo de execução inferior a 100 ms) para o script que bloqueia a renderização.
- RECOMENDAMOS separar a lógica de experimentos pesados do script de bloqueio de renderização. Carregue um stub pequeno que bloqueia a renderização e aplica a variante. Em seguida, carregue scripts de acompanhamento ou análise mais pesados separadamente com
asyncoudefer(semblocking="render"). - RECOMENDADO: use um atributo de dados em
<html>ou<body>para sinalizar a variante ativa e use seletores de CSS vinculados a esse atributo para definir o estilo da variante. Isso evita a manipulação direta do DOM no script que bloqueia a renderização. - NÃO use
blocking="render"em scripts de análise, rastreamento ou outros scripts não visuais. Somente os scripts que precisam mudar o que o usuário vê na primeira exibição devem bloquear a renderização. - NÃO combine
blocking="render"com snippets antioscilação legados. Eles resolvem o mesmo problema. Usar os dois cria atrasos desnecessários.
Estratégias de fallback
O atributo blocking não é compatível com todos os navegadores. Em navegadores que não oferecem suporte a ele, o atributo é ignorado e o script é carregado com o comportamento padrão (async nos exemplos acima), o que pode resultar em oscilação. É necessário um substituto para evitar oscilações em navegadores incompatíveis.
Alternativa: snippet antioscilação
FAÇA: use um snippet antioscilação leve como substituto apenas quando o blocking="render" não for compatível. Detecte o suporte ao recurso e pule o substituto em navegadores que o processam de forma nativa.
<head>
<!--
DO: Load the experiment script with blocking="render" for
browsers that support it. This is the preferred approach.
-->
<script
src="https://cdn.example.com/experiment-sdk.js"
async
blocking="render"
></script>
<script>
// DO: Only apply the anti-flicker fallback in browsers
// that do not support blocking="render".
if (!Object.hasOwn(HTMLScriptElement.prototype, 'blocking')) {
// Hide the page until the experiment script runs.
document.documentElement.classList.add('ab-loading');
// DO: Set a timeout to reveal the page if the experiment
// script takes too long. This prevents an indefinitely
// blank page on slow connections. Adjust the timeout
// to match your experiment SDK's expected load time.
setTimeout(() => {
document.documentElement.classList.remove('ab-loading');
}, 4000);
}
</script>
<style>
/*
DO: Use opacity to hide content during experiment loading.
This is only applied when blocking="render" is unsupported.
*/
.ab-loading {
opacity: 0 !important;
}
</style>
</head>
// DO: In your experiment SDK's initialization callback,
// remove the fallback class to reveal the page.
function onExperimentReady() {
document.documentElement.classList.remove('ab-loading');
}
Outras considerações
- Impacto no desempenho: o
blocking="render"troca a velocidade da primeira pintura pela correção visual. Monitore a Largest Contentful Paint (LCP) e a First Contentful Paint (FCP) para garantir que o script de experimento não esteja adicionando atrasos excessivos. - Confiabilidade do script de terceiros: se o SDK do experimento estiver hospedado em uma CDN de terceiros, uma interrupção na CDN poderá bloquear totalmente a renderização. O navegador aplica as próprias heurísticas de tempo limite (que podem ser maiores que 4 segundos), mas não há um tempo limite controlado pelo desenvolvedor para
blocking="render". Verifique se o provedor terceirizado tem garantias sólidas de tempo de atividade. - Alternativas do lado do servidor: para páginas em que a performance é essencial, considere o teste A/B do lado do servidor (em que o servidor renderiza a variante correta diretamente) em vez do teste do lado do cliente. As abordagens do lado do servidor eliminam completamente o flicker sem nenhum custo de bloqueio de renderização. Use o
blocking="render"do lado do cliente somente quando o teste do lado do servidor não for viável.