Anti-Flicker
How Coframe prevents a flash of original content while variants load
Copy this guide and your current settings as a prompt for your AI agent.
Preview agent promptSee the guide, code and settings included when you copy.
When Coframe applies a personalized or experiment variant to a page element, there is a window between when the browser first paints the page and when the SDK has finished fetching and applying its changes. Without any protection, visitors can briefly see the original content before it is replaced — a “flash of original content” that can look jarring and undermine the credibility of a variant-driven experience.
The anti-flicker mechanism addresses this by hiding the page until the SDK has either applied its changes or a timeout has elapsed.
On this page
01How It Works
Anti-flicker hides <body> immediately on page load using a CSS variable, then reveals it once the SDK has applied its changes or timeoutMs (default 1000 ms) has elapsed — whichever comes first.
The CSS variable approach lets the reveal be animated with a short opacity transition:
// Coframe antiflicker CSS
const style = document.createElement('style');
style.id = 'coframe-antiflicker';
style.textContent =
':root { --cf-show: 0;}' +
'body { opacity: var(--cf-show) !important; transition: opacity 0.2s ease; }'
function hide() { document.head.appendChild(style); }
function show() {
document.documentElement.style.setProperty('--cf-show', '1');
style.parentNode && style.parentNode.removeChild(style);
}
hide(); setTimeout(show, config.timeoutMs);When the SDK applies its changes, the body is revealed and the hiding style is removed. If the SDK fails to load for any reason, show() is still called, so the page is never permanently stuck in a hidden state.
02Why It Must Be Installed Synchronously
The anti-flicker snippet must run synchronously, before the browser paints content for the first time. If it is loaded through an async tag manager — or added with an async or defer attribute — the browser may already have painted the original content before the snippet runs, which means the first flash will have already occurred before the snippet can hide anything.
This is different from the main SDK script, which can be loaded asynchronously. The anti-flicker snippet is small by design and intentionally blocks the page parse for a brief moment so it can guarantee that body is hidden before paint.
03Timeout Behavior
The timeoutMs setting controls the maximum time the page will remain hidden. The default is 1000 ms. After that window closes, show() is called regardless of whether the SDK has finished applying changes.
This is a fail-open design: if the SDK is slow or unavailable, visitors will see the original content rather than a hidden page. The tradeoff is that in worst-case network conditions a visitor may briefly see unmodified content before a variant is applied, but the page is never stuck in a blank state.
You can tune the timeout through the SDK configuration. See SDK Options for the full list of runtime settings, including timeoutMs.
04When Anti-Flicker Is Disabled
Anti-flicker is automatically disabled when the SDK is running in trackingOnly: true mode. In tracking-only mode the SDK does not apply any content changes, so there is no variant to hide during load. Keeping anti-flicker active in that mode would add unnecessary latency without any benefit.
# Anti-Flicker
How Coframe prevents a flash of original content while variants load
When Coframe applies a personalized or experiment variant to a page element, there is a window between when the browser first paints the page and when the SDK has finished fetching and applying its changes. Without any protection, visitors can briefly see the original content before it is replaced — a “flash of original content” that can look jarring and undermine the credibility of a variant-driven experience.
The anti-flicker mechanism addresses this by hiding the page until the SDK has either applied its changes or a timeout has elapsed.
[Video: Video: the same page loaded with and without anti-flicker, slowed 10×, plus the timeoutMs fallback (42s, narrated)](assets/videos/fig04-anti-flicker.mp4)
Watch: 42s — the same page loaded with and without anti-flicker, slowed 10×, plus the timeoutMs fallback
## How It Works
Anti-flicker hides `<body>` immediately on page load using a CSS variable, then reveals it once the SDK has applied its changes or `timeoutMs` (default 1000 ms) has elapsed — whichever comes first.
The CSS variable approach lets the reveal be animated with a short opacity transition:
JS
```
// Coframe antiflicker CSS
const style = document.createElement('style');
style.id = 'coframe-antiflicker';
style.textContent =
':root { --cf-show: 0;}' +
'body { opacity: var(--cf-show) !important; transition: opacity 0.2s ease; }'
function hide() { document.head.appendChild(style); }
function show() {
document.documentElement.style.setProperty('--cf-show', '1');
style.parentNode && style.parentNode.removeChild(style);
}
hide(); setTimeout(show, config.timeoutMs);
```
When the SDK applies its changes, the body is revealed and the hiding style is removed. If the SDK fails to load for any reason, `show()` is still called, so the page is never permanently stuck in a hidden state.
## Why It Must Be Installed Synchronously
The anti-flicker snippet must run synchronously, before the browser paints content for the first time. If it is loaded through an async tag manager — or added with an `async` or `defer` attribute — the browser may already have painted the original content before the snippet runs, which means the first flash will have already occurred before the snippet can hide anything.
This is different from the main SDK script, which can be loaded asynchronously. The anti-flicker snippet is small by design and intentionally blocks the page parse for a brief moment so it can guarantee that `body` is hidden before paint.
## Timeout Behavior
The `timeoutMs` setting controls the maximum time the page will remain hidden. The default is 1000 ms. After that window closes, `show()` is called regardless of whether the SDK has finished applying changes.
This is a fail-open design: if the SDK is slow or unavailable, visitors will see the original content rather than a hidden page. The tradeoff is that in worst-case network conditions a visitor may briefly see unmodified content before a variant is applied, but the page is never stuck in a blank state.
You can tune the timeout through the SDK configuration. See [SDK Options](../docs/sdk-options.html) for the full list of runtime settings, including `timeoutMs`.
## When Anti-Flicker Is Disabled
Anti-flicker is automatically disabled when the SDK is running in `trackingOnly: true` mode. In tracking-only mode the SDK does not apply any content changes, so there is no variant to hide during load. Keeping anti-flicker active in that mode would add unnecessary latency without any benefit.