Why it happens
getVariant() is synchronous and needs experiment config to answer correctly. Where that
config comes from differs by visitor:
- Returning visitor — config is in local storage from a previous visit, read synchronously before first paint. Correct immediately, no flicker.
- First-time visitor — no cache. The SDK returns
controlwhile the first config fetch is in flight, then re-renders with the real assignment when it lands.
Fix 1 — Resolve on the server (best)
If you’re on Next.js, the server can resolve variants before any HTML is sent, so the first paint is already correct for everyone. Nothing is hidden and there’s no timeout to tune.resolveExperiments() from @trevosdk/browser/server.
Fix 2 — Anti-flicker snippet
If you can’t resolve server-side, hide the page until variants are ready. Inline this in<head>, before anything renders:
body { opacity: 0 } and reveals automatically once config arrives — or after a
1 second safety timeout, whichever comes first.
That timeout is the important part. If an ad blocker, a CSP rule, or a network failure stops
the SDK loading, the page reveals anyway rather than staying blank. Raise it if your users are
on slow networks, but understand the trade:
manualReveal: true and call trevo.reveal() yourself once
you’ve committed the variant.
Fix 3 — Design around it
Often the cheapest answer. A first-paint swap is only visible if the tested element is visible on first paint.- Test things below the fold, or behind an interaction — a modal, a second step, a menu
- Gate rendering on readiness where the surface can wait:
- Or
await trevo.ready()before rendering the section under test