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

Feature Flags in Your Next.js SaaS -- Gradual Rollouts, Private Betas, and Kill Switches Without LaunchDarkly

October 3, 2026
nextjsdrizzle-ormsaasboilerplatelaunch

The Deploy That Broke Everything

You finished the feature. Tests pass. It works on your machine. You deploy.

Forty minutes later a user emails you: "The dashboard is blank." You check your test account -- fine. You check three others -- fine. Six customers are looking at a blank screen because the feature works unless they have more than 500 rows in their data table.

You just learned what feature flags are for.

What LaunchDarkly Costs vs What You Already Have

LaunchDarkly starts at $20 per month for hobby use and climbs past $200 for a real team. It is a good tool. It is also three database columns you already have in your Neon DB.

The three patterns every SaaS actually needs:

  1. Per-user flag -- turn a feature on for specific users (beta testers, paying tiers, your own account)
  2. Percentage rollout -- ship to 10% of users, watch error rates, expand to 100% when confident
  3. Global kill switch -- turn a feature off for everyone immediately without a deploy

You can build all three in an afternoon with the Next.js SaaS Boilerplate already in place.

The Schema

Add a featureFlags table to your Drizzle schema:

export const featureFlagTable = pgTable('feature_flags', {
  id: uuid('id').defaultRandom().primaryKey(),
  name: varchar('name', { length: 100 }).notNull().unique(),
  enabled: boolean('enabled').notNull().default(false),
  rolloutPercent: integer('rollout_percent').notNull().default(0),
  allowedUserIds: text('allowed_user_ids').array().notNull().default([]),
  createdAt: timestamp('created_at').defaultNow().notNull(),
});

One row per flag. enabled: false is the kill switch. rolloutPercent: 10 enables the flag for a stable 10% of your user base. allowedUserIds handles private betas where specific users get early access.

The Service Check

In your service layer, a single function decides whether a flag is active for a given user:

export async function isFlagEnabled(flagName: string, userId: string): Promise<boolean> {
  const flag = await featureFlagRepo.findByName(flagName);
  if (!flag || !flag.enabled) return false;
  if (flag.allowedUserIds.includes(userId)) return true;
  if (flag.rolloutPercent >= 100) return true;
  if (flag.rolloutPercent === 0) return false;
  const hash = userId.split('').reduce((acc, c) => acc + c.charCodeAt(0), 0);
  return (hash % 100) < flag.rolloutPercent;
}

The hash assigns each user to a stable bucket so they do not see the feature appear and disappear on each page load. A user in the 10% rollout stays in that group every time.

Using Flags in Server Components

Because your data fetching lives in server components, the flag check is a single await:

const showNewDashboard = await isFlagEnabled('new_dashboard', user.id);

Pass the result as a prop to the component that needs it. No client-side requests, no layout shift, no flicker.

Managing Flags Without a UI

You can manage flags directly from Drizzle Studio (npm run db:studio) during development. For production, a flags tab inside your admin panel takes about 30 minutes to add -- a table listing flags with a toggle button per row.

The Private Beta Workflow

Here is the sequence that works in practice:

  1. Create a flag row with enabled: true and rolloutPercent: 0
  2. Add your beta users' IDs to allowedUserIds
  3. Run the beta, collect feedback, fix the issues that come up
  4. Set rolloutPercent to 10 and clear allowedUserIds
  5. Watch your error monitoring for 24 hours
  6. Bump to 50%, then 100%
  7. Delete the flag row and remove the conditional from the code

Step 7 is the one founders skip. Flags that are never cleaned up become invisible tech debt -- you will not remember in six months what the new_dashboard_v3 flag controls. Keep a list and remove each one once its rollout reaches 100%.

When to Use Flags (and When Not To)

Use flags for: schema changes that affect UI, new pricing experiments, features you are not ready to support publicly, anything that touched code paths you cannot easily test with your own account.

Skip flags for: bug fixes, security patches, cosmetic changes. A flag adds an extra code path that has to be tested and eventually removed. The risk of shipping a CSS tweak to everyone is lower than the ongoing cost of an unused flag.

What This Gives You on Day One

Once this is in place you have a real deployment safety net. You can ship on a Friday afternoon because a broken feature affects 5% of users instead of all of them. You can give paying customers early access to features still in beta. You can turn something off the moment a user reports an issue without touching the codebase.

None of that requires a third-party service. It requires a table, a function, and the discipline to clean flags up after rollout.

Launch With the Right Foundation

The Next.js SaaS Boilerplate ships with Drizzle ORM, Neon DB, and the service-layer architecture that makes a check like isFlagEnabled a natural fit. Auth, payments, and email are already wired -- so adding feature flags is a migration and a function, not a week of integration work.

Ship your next feature to 5 users first. Get the boilerplate and launch your idea this weekend.