What Flicker Is and Why It Happens
Flicker in Optimizely — technically called FOOC, Flash of Original Content — is when a visitor briefly sees the original page before the experiment variation loads. It looks like a page flash or jump, usually lasting 50–500ms. Users notice it. It erodes trust. And in extreme cases, it invalidates your experiment results because visitors see the control before the treatment.
The root cause is timing: your page's HTML and CSS render before Optimizely's JavaScript has a chance to apply variation changes. Browsers are optimized to render content fast. Optimizely needs to intercept that render and make changes — and if there's any delay in loading or executing the Optimizely snippet, the original content flashes first.
The Three Types of Flicker
Not all flicker is the same, and diagnosing which type you have determines which fix applies:
1. FOOC (Flash of Original Content) — The variation element briefly appears in its original state before switching to the variation. This is the classic flicker. A button changes from blue to red, but you see blue for 100ms first.
2. Layout Shift — The page loads, content shifts as Optimizely adds or removes elements. Particularly common when testing element insertion, removal, or repositioning. Also impacts your Core Web Vitals CLS score.
3. Font Flash / Style Flash — Fonts or styles change mid-render. Happens when variation CSS is applied after the browser has already laid out text.
Root Causes
Before applying fixes, identify the actual cause:
- Async snippet loading too late — The Optimizely snippet is placed too far down the page, or it's being loaded asynchronously in a way that creates a race condition with page render
- DOMContentLoaded firing before Optimizely activates — For experiments using DOM manipulation, if the DOM is ready before Optimizely executes, you get a brief window where the original DOM is visible
- Slow CDN response — Optimizely's snippet comes from their CDN. On bad network days or slow CDNs, even a correctly placed snippet can delay
- Incorrect snippet placement — Snippet in the wrong location in the head tag, below other blocking scripts, or conditional loaded
- Variation code complexity — Complex variation changes that take significant JavaScript execution time to apply
Fix 1: Synchronous Snippet Placement
The most effective fix is ensuring the Optimizely snippet loads synchronously in the head of your page, before any other scripts and before the body tag.
The snippet should be the FIRST script in the head section — place it before other scripts.
When this is appropriate: When your experiments require DOM manipulation that must complete before first paint. This is the gold standard placement.
Performance tradeoff: A synchronous snippet blocks page rendering until it downloads and executes. Optimizely's snippet typically ranges from 50–200KB gzipped depending on the number of active experiments. On a fast connection, this adds 20–80ms of render-blocking time. On slow mobile connections, it can add 200–500ms. This is a real cost — weigh it against the cost of flicker.
**Pro Tip:** If you're seeing 300ms+ render-blocking from a synchronous snippet, your experiment count may be too high. Optimizely loads all active experiments in one snippet. Pausing or archiving idle experiments reduces snippet size and blocking time.
Fix 2: The Anti-Flicker Snippet
Optimizely's anti-flicker snippet (also called the async hiding snippet) is a small piece of code that hides the page body until Optimizely has had a chance to apply variations, then reveals it. It's designed for situations where you want async snippet loading (for performance) but need to prevent flicker.
How it works: The snippet immediately hides the page (sets opacity to 0), loads Optimizely asynchronously, and then Optimizely reveals the page after applying variations. There's a built-in timeout (default 3 seconds) that reveals the page even if Optimizely fails to load — preventing users from seeing a blank white page.
Performance tradeoff: The anti-flicker snippet still hides page content during Optimizely's loading window, which impacts LCP. Users don't see flicker, but they do see a blank page briefly. The net effect on user experience is debatable — some teams find users prefer a slight delay over a visible flash.
**Pro Tip:** The default 3-second timeout in the anti-flicker snippet is too long. Set it to 1,500ms maximum. If Optimizely hasn't loaded in 1.5 seconds, something is wrong with your implementation, and showing a blank page for 3 seconds is worse than flicker.
Fix 3: Timeout Tuning
Inside Optimizely's project settings and within the anti-flicker snippet, you can adjust timeout values. There are two relevant timeouts:
Anti-flicker timeout: How long the page stays hidden waiting for Optimizely. Default is 3,000ms. Reduce to 1,000–1,500ms.
Activation timeout: For manually activated experiments, how long Optimizely waits for your activate() call. Ensure this is set appropriately for your page load timing.
Fix 4: Lazy Loading Fallback
For experiments that add content or restructure layout, prevent layout shift by pre-reserving space for variation elements using CSS. If your experiment replaces a hero image, reserve the hero image dimensions even before Optimizely loads:
Set a min-height on your container element matching your variation's expected height, and add a placeholder background color. This doesn't prevent flicker of the content itself, but it prevents the page from jumping when Optimizely swaps elements — which is the layout shift variety of flicker.
Diagnosing Which Fix You Need: The 3-Step Checklist
Step 1: Check snippet placement. Open DevTools > Network tab. Filter by your Optimizely project ID. Is the snippet loading before DOMContentLoaded? If the snippet appears after DOMContentLoaded fires, you have a placement problem. Fix: move the snippet to the top of the head section.
Step 2: Check snippet load time. In the same Network tab, what's the duration for the Optimizely snippet request? If it's consistently above 100ms, you have a CDN latency or network issue. Fix: Consider async loading with anti-flicker snippet to unblock render.
Step 3: Check variation complexity. In DevTools > Performance tab, record a page load. Look for JavaScript execution time attributed to Optimizely. If variation code execution itself takes 100ms+, the fix is optimizing variation JavaScript, not snippet placement.
**Pro Tip:** Record a page load in Chrome DevTools Performance tab with CPU throttling set to 4x slowdown. This simulates a mid-tier Android device and makes flicker obviously visible if it exists. Always test on throttled CPU and slow network before launching.
When Flicker Is a Symptom of a Bigger Problem
If you've tried all fixes and still see persistent flicker, the issue may be architectural:
- Server-side rendering conflicts: If your site uses SSR (Next.js, Nuxt, Astro), Optimizely's client-side DOM manipulation fights with pre-rendered HTML. Consider server-side experimentation via Optimizely's Feature Experimentation product.
- CDN caching the wrong content: If your CDN (Cloudflare, Fastly) is caching pages without the Optimizely snippet or caching variation content, results will be inconsistent. Ensure your cache rules exempt experiment-modified pages.
- Too many active experiments: Each additional active experiment adds to snippet size and execution time. Run a snippet audit — pause or archive everything you're not actively monitoring.
What to Do Next
- Open DevTools on your experiment page and run the 3-step diagnostic above
- Start with Fix 1 (synchronous placement in the head section) — it solves 80% of cases
- If you need async loading for performance reasons, implement the anti-flicker snippet with a 1,500ms timeout
- Reduce timeout on the anti-flicker snippet if your Optimizely snippet loads consistently under 1 second
- For layout shift specifically, pre-reserve element dimensions with CSS
If you're seeing performance concerns alongside the flicker issues, see Optimizely Snippet Performance: How Much Does It Slow Down Your Site? for the full impact analysis.