Claude Code Boilerplate
FeaturesPricingBlogDocs
Get started →

Product

  • Features
  • Pricing
  • Skills

Compare

  • vs ShipFast
  • vs MakerKit
  • vs supastarter

Resources

  • Docs
  • Blog
  • Discord

Legal

  • License
  • Privacy Policy
  • Terms of Service
Claude Code Boilerplate

© 2026 Claude Code Boilerplate. All rights reserved.

← All posts

A/B Testing in Your Next.js SaaS -- Test Pricing Pages and Onboarding Without Paying for Optimizely

July 26, 2026
nextjsdrizzle-ormsaasanalyticsboilerplate

You changed your pricing page last month. Conversions went up or stayed flat -- you are not sure which, and you would not know why either way. That is the A/B testing problem: without it, every change is a guess.

The standard answer is Optimizely or VWO, which run $500 to $2,000 per month. That is a lot to spend before you have proven any of your pricing hypotheses. There is a simpler path.

What You Actually Need From A/B Testing

Most SaaS founders only need three things from A/B testing:

  1. Randomly assign a variant to each visitor -- and keep them in that variant on repeat visits
  2. Record which variant was shown when a conversion event happens
  3. Count conversions per variant so you can pick a winner

That is it. You do not need a visual editor. You do not need multivariate testing. You do not need heatmaps. You need random assignment, persistence, and outcome tracking.

The Database Schema

Two tables. One stores experiments and their variants. One stores assignments -- which variant each user saw and whether they converted.

-- experiments: id, name, variants (text[]), status, created_at
-- experiment_assignments: id, experiment_id, user_id, variant, converted_at

With Drizzle ORM this is a straightforward schema addition to your existing Next.js SaaS. The assignment table is the most important part -- once a user is assigned a variant, every subsequent request reads from this table to serve the same experience.

How Assignment Works

When a user hits a page that is inside an experiment:

  1. Look up their existing assignment for this experiment in Drizzle ORM
  2. If none exists, pick a variant at random and write it to the assignments table
  3. Return the variant -- either control or treatment

The service layer handles this as a single upsert. The route handler reads it and passes the variant as a prop to the page.

This pattern works with Next.js App Router server components. No client-side flicker, no layout shift, and no need for a third-party cookie-based workaround. The assignment query runs on the server alongside your other data fetches.

Tracking Conversions

A conversion is any action you care about: completing signup, clicking the upgrade button, finishing onboarding. When that action completes, your service layer sets converted_at on the assignment record.

// Inside your upgrade service -- after the Stripe checkout session is created
await experimentService.recordConversion(userId, 'pricing-page-v2')

That is one extra call inside the function that already handles the business event. You do not touch your route handler. You do not add any client-side tracking script. The conversion attaches to the same Drizzle record that stored the assignment.

Reading Results

A simple query aggregates assignments by variant and counts converted_at IS NOT NULL. You can surface this in a Recharts chart inside your admin panel, or query it directly in Drizzle Studio while the experiment runs.

The number you care about is conversion rate per variant. Once you have at least 100 conversions per variant, you can pick a winner and ship the change permanently.

What to Test First

Start with the highest-leverage pages:

  • Pricing page -- headline copy, plan names, CTA button text, or adding a money-back guarantee badge
  • Onboarding step 1 -- the first screen after signup, where most users drop off before seeing any value
  • Upgrade prompt -- the modal or banner that asks free users to become paying customers

Each test should change one thing. Do not test a completely redesigned page against the original -- you will not know what caused the difference. One headline, one button label, one section added or removed.

One Thing to Watch

Sample-ratio mismatch is the most common mistake in a DIY A/B system. If your random assignment is not truly 50/50 -- for example because you assign on the pricing page but only track conversions for logged-in users -- your results will be misleading.

Before you declare a winner, check the raw assignment counts per variant. If one variant has 20% more assignments than the other, the random assignment or the eligibility logic has a bug. Fix it before drawing any conclusions.

Pairing With Feature Flags

If you already use database-backed feature flags in your SaaS, A/B experiments slot in as a natural extension. A flag controls whether an experiment is active. The experiment assignment controls which variant an active user sees. You can start and stop experiments without a redeploy, and the same service-layer pattern handles both.

The Minimal Build

For an early-stage SaaS, this system is four things:

  • Two Drizzle ORM tables (experiments + assignments)
  • One service function that reads or creates an assignment (upsert on conflict)
  • One service function that records a conversion (update converted_at)
  • One admin query that groups by variant and counts conversions

The Next.js SaaS boilerplate already gives you the Drizzle ORM setup, the service-layer conventions, and the admin panel shell. This module fits on top without changing any of your existing code.

A 10% conversion lift on your pricing page is worth more than the hours it takes to build this. Run your first test before you spend another week changing copy by instinct.

Get the boilerplate and ship your first A/B test this weekend.