What happens to Framer A/B tests when you convert to code

How a Framer split test survives a conversion to Next.js and other frameworks, how visitors are assigned, where to set weights, and how to measure results.

Published
Reading time
5 min
Deframer cover: “A/B tests, still running.” Beside it, two cards, Variant A or Variant B, with Variant A selected
On this page9 sections

When you convert a Framer site that runs an A/B test, Deframer keeps the test running in code: the original page stays at its URL, every variant is prerendered alongside it, and a small piece of server code assigns each visitor a variant and remembers it in a cookie for 30 days. Links that force a variant with ?framer_variant= keep working. Two things don't come along: Framer's weights, which aren't published, so every variant gets an equal share you can edit, and Framer's test results, which stay in Framer. You measure the converted test with your own analytics.

A static Vite export is the exception: with no server to choose a variant per visitor, it ships the original page only. Deframer is an independent tool and is not affiliated with, endorsed or sponsored by Framer.

How Framer runs a split test

In Framer, a split test turns one page into several variants that share its URL. Framer's server picks which variant a visitor gets and serves it at the page's address; in the browser, Framer's runtime puts that variant in the original's place. The weights live in Framer's server configuration, and the results show in Framer's analytics.

From the outside, all you can see is that the same URL may return different pages. Deframer reads every variant from the published site, along with the variant names as the editor shows them.

What the converted project contains

In a Next.js project, the pieces are:

Piece

Where

What it does

The original page

Its normal route, such as src/app/pricing/page.tsx

Served to its share of visitors

Each variant

src/app/split-tests/<page>/<variant>/…

Prerendered like any page

The assignment

src/proxy.ts

Picks a variant and rewrites the URL to its folder

The weights

src/site/split-tests.ts

Each variant's share, editable

The variant folders aren't URLs of the site: requested directly they return 404, they're in no sitemap, and each variant's canonical URL is the page's own. To visitors and search engines there is one page at one URL, as on Framer.

Variants are named after their editor names ("b2b-saas") when they have one, or by letter ("b", "c") when Framer's default "Variant" name says nothing. The original is the first share.

The TanStack Start, Astro and React Router projects serve split tests too, through their own server code; the files differ but the behavior is the same.

How is a visitor assigned a variant?

The proxy decides in this order:

  1. A forced variant. ?framer_variant=<id> in the URL picks that variant, exactly as Framer's server does. Ads and links you built for the published site keep working.
  2. A returning visitor. If the visitor already has the test's cookie, they get the same variant again.
  3. A new visitor. A random pick by weight.

The visitor then keeps the variant in a cookie named after the test (for example split-test-pricing) for 30 days, so they don't see the page change between visits.

Because assignment happens on the server before the page is sent, there's no flash of the original before the variant appears.

Why are the weights equal?

Framer doesn't publish its split-test weights: they're server configuration, not part of the site anyone can read. So the converter gives every variant, the original included, an equal share and writes the weights into src/site/split-tests.ts where you can see them.

If your Framer test ran at, say, 80/20, set the same split there. Changing weights only affects new visitors; anyone with a cookie keeps their variant.

What doesn't carry over?

  • Results. Conversions, visitors and significance stay in Framer's analytics. They aren't part of the published site.
  • Weights, as above.
  • Framer's test controls. Starting, stopping or picking a winner is now a code change, not a button.
  • Static hosting. The Vite export ships each tested page's original version, because there's no server to assign visitors.

How to measure a converted test

Assignment without measurement isn't a test. You need two things in your analytics: which variant a visitor saw, and whether they converted.

  1. Record the variant. Read the test's cookie in the browser and send it with your page view or as a user property. With most tools that's one call after the page loads, for example a custom property on the page view event.
  2. Record the goal. A form submission, a click on "Book a call", a sign-up. If the goal is a native Framer form, it posts to the project's own API route, so you can also count submissions server-side.
  3. Compare. Conversion rate per variant, with enough visitors to be meaningful. Your analytics tool's experiment or funnel reports, or a simple export and a significance calculator, will do.

If your analytics came over in Framer's custom code, it's already on every page; see analytics after moving off Framer for the details and alternatives. Some tools, such as PostHog, have feature flags and experiments of their own; you can move assignment there instead of the proxy if you prefer one system.

How to end a test

When you've picked a winner:

  1. Make the winning variant the page. If it's a variant, copy its view over the original's (or point the route at the variant's view).
  2. Remove the test from src/site/split-tests.ts and delete the variant folders.
  3. Build, and check that the page's URL returns the winner and the old variant paths return 404.
  4. Optionally clear the cookie by letting it expire; it does nothing once the test is gone.

Commit each step on a branch so you can see exactly what changed, as described in maintaining a converted site with Git.

Checking a test before launch

When live previews are on, the conversion's result page lists the site's split tests and lets you view each variant in the compare view. Locally, you can do the same with the forcing parameter:

sh
npm run build && npm start
curl -s "http://localhost:3000/pricing?framer_variant=<variant id>" | grep -o '<title>[^<]*'

Then check:

  • the page URL serves a variant to a fresh browser and the same one on reload;
  • every variant forced by URL renders correctly at desktop, tablet and phone widths;
  • the variant folder paths return 404;
  • sitemap.xml lists the page once.

FAQ

Does Deframer keep Framer split tests?

Yes, in the Next.js, TanStack Start, Astro and React Router projects. The original and every variant are prerendered, and server code assigns visitors and remembers their variant for 30 days. A Vite export ships the original only.

Will my test results come with the conversion?

No. Results live in Framer's analytics, which isn't part of the published site. Export or note them before you cancel your Framer plan.

Can I set the same traffic split I used in Framer?

Yes. Framer's weights aren't published, so the project starts with equal shares; set your own in src/site/split-tests.ts.

Do links with ?framer_variant still work?

Yes. The proxy honors ?framer_variant=<id> the same way Framer's server does, so links and ads that force a variant keep working.

Is a split test bad for SEO?

Not when it's done as here: one URL, server-side assignment, and every variant's canonical pointing at the page. Search engines see one page. Don't leave a test running for months; pick a winner and remove the others.