OneApp Docs
PackagesFeatures

@repo/feature-flags

Turn features on or off without deploying code. Gradually roll out to 5%, 25%, 50% of users. Run A/B tests. Instantly disable broken features. Evaluated at the edge with zero latency.

Quick Start

Add feature flags in 5 minutes:

pnpm add @repo/feature-flags

Check flags in code, enable for specific users, run experiments. Skip to Quick Start →

Why @repo/feature-flags?

Deploying features is risky. You can't test in production until you deploy, but deploying means all users see the change. If something breaks, you need to redeploy to fix it. A/B testing requires complex infrastructure.

@repo/feature-flags solves this by decoupling code deployment from feature activation. Deploy code safely, enable for 1% of users, validate, then expand to 100%.

Production-ready with edge evaluation (zero latency), user targeting, percentage rollouts, A/B testing, and instant kill switches.

Use cases

  • Gradual rollouts — Launch to 5% of users, then 25%, then 100%
  • A/B testing — Test two pricing pages, measure which converts better
  • Beta access — Give paying customers early access to new features
  • Kill switches — Instantly disable broken features without redeploying
  • User targeting — Premium features only for paid users, regional features

How it works

@repo/feature-flags provides server and client APIs for checking feature flags:

import { getFlag } from "@repo/feature-flags/server";

// Check if feature is enabled
const newCheckoutEnabled = await getFlag("new-checkout");

if (newCheckoutEnabled) {
  return <NewCheckout />;
} else {
  return <OldCheckout />;
}

Flags are stored in Vercel Edge Config and evaluated at the edge with zero latency.

Key features

Zero-latency evaluation — Flags checked at the edge, no API calls, works offline

Percentage rollouts — Show to 10%, 25%, 50% of users, consistent per user

User targeting — Enable for specific users, segments, or organizations

A/B testing — Multiple variants with automatic exposure tracking

Instant updates — Change flags from dashboard, no deployment needed

Local overrides — Test in development with URL parameters or env vars

Quick Start

1. Install the package

pnpm add @repo/feature-flags @vercel/flags @vercel/edge-config

2. Configure Edge Config connection

.env.local
EDGE_CONFIG=https://edge-config.vercel.com/xxxxx

3. Check a feature flag (server)

app/page.tsx
import { getFlag } from "@repo/feature-flags/server";

export default async function HomePage() {
  const newDashboardEnabled = await getFlag("new-dashboard");

  if (newDashboardEnabled) {
    return <NewDashboard />;
  }

  return <OldDashboard />;
}

4. Check a feature flag (client)

app/components/FeatureGate.tsx
"use client";

import { useFlag } from "@repo/feature-flags/client";

export function FeatureGate() {
  const betaChatEnabled = useFlag("beta-chat");

  if (!betaChatEnabled) return null;

  return <BetaChat />;
}

That's it! Your app now supports feature flags with zero-latency edge evaluation.

Run an A/B test

Test multiple variants and track exposure:

const { variant, trackExposure } = await getExperiment("pricing-test");

await trackExposure(); // Track that user saw this variant

if (variant === "control") return <OriginalPricing />;
if (variant === "variant-b") return <NewPricing />;

Distribution

This package is available as @oneapp/feature-flags for use outside the monorepo.

npm install @oneapp/feature-flags

Build configuration: Uses tsdown with createDistConfig('react', ...) for distribution builds.


Technical Details

For Developers: Technical implementation details

Feature flag management for gradual rollouts, A/B testing, and feature toggling. Supports multiple providers and local overrides.

Installation

pnpm add @repo/feature-flags

Prerequisite

Requires Vercel Edge Config for production deployments.

Overview

PropertyValue
Locationpackages/feature-flags
Dependencies@vercel/flags, @vercel/edge-config
ProvidersVercel Edge Config, Environment

Export Paths

PathDescription
@repo/feature-flagsMain exports
@repo/feature-flags/serverServer-side utilities
@repo/feature-flags/clientClient-side utilities

Basic Usage

Check Flag (Server)

import { getFlag } from "@repo/feature-flags/server";

// highlight-next-line
const isEnabled = await getFlag("new-feature");

if (isEnabled) {
  // Show new feature
}

Check Flag (Client)

"use client";

import { useFlag } from "@repo/feature-flags/client";

function MyComponent() {
  // highlight-next-line
  const isEnabled = useFlag("new-feature");

  if (!isEnabled) return null;

  return <NewFeature />;
}

Flag Types

Boolean Flags

// highlight-next-line
const isEnabled = await getFlag<boolean>("feature-enabled");

String Flags

// highlight-next-line
const variant = await getFlag<string>("button-color");
// "blue" | "green" | "red"

Number Flags

const limit = await getFlag<number>("api-rate-limit");

JSON Flags

interface FeatureConfig {
  enabled: boolean;
  maxItems: number;
  allowedUsers: string[];
}

// highlight-next-line
const config = await getFlag<FeatureConfig>("advanced-config");

User Targeting

Personalization

Target specific users or user segments for personalized feature rollouts.

import { getFlag } from "@repo/feature-flags/server";

// highlight-start
const isEnabled = await getFlag("premium-feature", {
  userId: user.id,
  email: user.email,
  plan: user.plan
});
// highlight-end

// Flag rules can target specific users or segments

Percentage Rollouts

// In flag configuration:
// highlight-start
// {
//   "beta-feature": {
//     "percentage": 25,
//     "enabled": true
//   }
// }
// highlight-end

// 25% of users will see the feature
const isEnabled = await getFlag("beta-feature", {
  userId: user.id // Used for consistent bucketing
});

A/B Testing

import { getExperiment } from "@repo/feature-flags";

// highlight-start
const { variant, trackExposure } = await getExperiment("pricing-test", {
  userId: user.id,
});

// Track that user saw the variant
await trackExposure();
// highlight-end

if (variant === "control") {
  return <OriginalPricing />;
} else if (variant === "variant-a") {
  return <NewPricingA />;
} else {
  return <NewPricingB />;
}

React Provider

// app/layout.tsx
import { FlagsProvider } from "@repo/feature-flags/client";

export default async function RootLayout({ children }) {
  // highlight-next-line
  // Pre-fetch flags on server
  const flags = await getAllFlags();

  return (
    <FlagsProvider value={flags}>
      {children}
    </FlagsProvider>
  );
}

useFlags Hook

"use client";

import { useFlags } from "@repo/feature-flags/client";

function FeatureGate() {
  // highlight-next-line
  const flags = useFlags();

  return (
    <div>
      {flags["new-dashboard"] && <NewDashboard />}
      {flags["beta-chat"] && <BetaChat />}

  );
}

Middleware

Edge Runtime

Flags are evaluated at the edge for zero-latency feature toggling.

// middleware.ts
import { withFeatureFlags } from "@repo/feature-flags/server";

// highlight-start
export const middleware = withFeatureFlags(async (request, flags) => {
  if (flags["maintenance-mode"]) {
    return NextResponse.redirect(new URL("/maintenance", request.url));
  }

  return NextResponse.next();
});
// highlight-end

Local Overrides

Development

// .env.local
FEATURE_FLAGS_OVERRIDE = { "new-feature": true, "beta-feature": false };

URL Overrides

Development Only

URL overrides should only be enabled in development or for authorized users.

// Enable for testing via URL
// https://app.example.com?ff_new-feature=true

import { withUrlOverrides } from "@repo/feature-flags/client";

// highlight-next-line
const flags = withUrlOverrides(serverFlags);

Flag Definitions

// flags.ts
import { defineFlags } from "@repo/feature-flags";

// highlight-start
export const flags = defineFlags({
  "new-dashboard": {
    description: "New dashboard redesign",
    defaultValue: false,
    type: "boolean"
  },
  "api-version": {
    description: "API version to use",
    defaultValue: "v1",
    type: "string",
    options: ["v1", "v2", "v3"]
  }
});
// highlight-end

Edge Config Integration

import { createEdgeConfigClient } from "@repo/feature-flags";

// highlight-start
const client = createEdgeConfigClient({
  connectionString: process.env.EDGE_CONFIG
});

const flags = await client.getAll();
// highlight-end

Environment Variables

Required for Production

EDGE_CONFIG connection string is required for production feature flags.

# Vercel Edge Config
EDGE_CONFIG="https://edge-config.vercel.com/..."

# Feature flag overrides (development)
FEATURE_FLAGS_OVERRIDE={"flag-name":true}

On this page