Your checkout component renders perfectly. The layout looks right, the tax rate loads, the payment methods appear. Everything passes your visual check — and then you deploy, and users get the experimental beta layout when they shouldn't.
The bug? A feature flag with the value "false" — a string, not a boolean. In JavaScript, a non-empty string is truthy. So the flag that was supposed to disable the experimental UI was quietly enabling it for every single user. TypeScript had no idea, because it had no idea your feature flags existed at all.
This is the quiet danger of untyped feature flags. They fail silently, they're hard to reproduce in tests, and they tend to surface at the worst possible moment. Here's how to close that gap with generated TypeScript types — and a few additional best practices that make your flags easier to maintain as your codebase grows.
Why TypeScript Doesn't Save You (By Default)
Most React developers working with feature flags write something like this:
const { isExperimental, taxRate, headline, paymentMethods } = useGrowthBook();This looks reasonable. But TypeScript has no way of knowing:
- Whether
isExperimentalis a real flag name in GrowthBook - Whether it should be a boolean, a string, or a number
- Whether your fallback value type matches the default defined in your dashboard
Without type definitions, the SDK treats everything as any. You can pass a string where a boolean belongs, misspell a flag name, or reference a flag that's been deleted — and your code compiles without complaint. The result is a whole category of bugs that are genuinely hard to catch: everything looks fine at the TypeScript layer, but the runtime behavior is wrong.
The Fix: Generated Type Definitions for Your Feature Flags
The solution is to give the GrowthBook React SDK a TypeScript interface that describes all your flags — their names and their value types — so the compiler can enforce correctness for you.
GrowthBook provides a CLI tool to generate these types. But if you're using Cursor or another AI-assisted editor with MCP support, there's an even faster path: you can generate the types directly through GrowthBook's MCP server without leaving your editor.
Either way, the result is a file called app-features.ts that contains a complete TypeScript interface for every flag in your GrowthBook account:
export interface AppFeatures {
checkout_experimental_layout: boolean;
headline: string;
shipping_tax: number;
payment_methods: string[];
}Every flag. Every type. Automatically generated from your actual GrowthBook configuration — not hand-written and left to drift.
Using the Flag Types in Your Component
Once you have app-features.ts, import it and pass it to the useGrowthBook hook as a generic type parameter:
import { AppFeatures } from './appfeatures';
import { useGrowthBook } from '@growthbook/growthbook-react';
const gb = useGrowthBook<AppFeatures>();That one change unlocks the full power of TypeScript's type checker against your feature flags. The moment you do this, errors that were previously invisible become immediately visible — right in your editor, before you run anything.
The Errors You'll Actually See
When we applied types to a real checkout component with four flags, TypeScript surfaced several problems immediately:
Wrong flag name. The component was using isExperimental as a flag key. The actual flag in GrowthBook is checkout.experimental_layout. Without types, this compiled and ran fine — it just returned the fallback value every time, silently. With types, it's a compiler error on the spot.
Wrong value type. The fallback for checkout.experimental_layout was "false" — a string. The actual flag type is boolean. This is the bug from the opening: because "false" is a truthy string, the experimental layout was enabled for every user. TypeScript catches this the moment you add the type definition.
Mismatched default values. The component assumed payment_methods defaulted to just credit card. The actual default in GrowthBook includes Bitcoin. With the MCP server, you can verify that your fallback values match your GrowthBook defaults directly in the editor — and even have the agent update the code for you.
These aren't hypothetical bugs. They're the kind of thing that gets deployed on a Friday.
Keeping Flag Types in Sync
Generating types once is useful. Keeping them current is what makes this a real system.
When you generate types via the GrowthBook CLI or MCP server, it also adds a script to your package.json:
"scripts": {
"generate-flag-types": "growthbook generate-types"
}Run this any time you add, remove, or change a flag in GrowthBook. It takes seconds and ensures your TypeScript definitions never drift from your actual configuration. A good practice: add it to your CI pipeline, or at minimum to your pre-release checklist. Stale type definitions are better than none, but fresh ones are what give you the full safety guarantee.
Three More Feature-Flag Best Practices Worth Adding
Type safety solves the hardest category of feature flag bugs, but there are a few additional practices that will save you headaches as your flag usage grows.
Handle Loading States Explicitly
When your app initializes, GrowthBook fetches flag values from the server. During this brief window, the SDK relies on local fallback values. If not handled explicitly, this can result in a "flash of unstyled content" (FOUC) where users see the wrong UI state for a split second.
To solve this, the GrowthBook React SDK provides the <FeaturesReady> helper component. It allows you to render a loading state until your features are fully loaded:
<FeaturesReady timeout={500} fallback={<LoadingSpinner/>}>
<ComponentThatUsesFeatures/>
</FeaturesReady>Don't skip this. While loading is often near-instant in development, the "flash" becomes painfully obvious for production users on slower connections
Use Descriptive, Consistent Flag Names
Flag names like ff-123 or new-ui become unmaintainable fast. When you have 50 flags, you need to know at a glance what each one controls, which team owns it, and whether it's still active.
A naming convention that works well: {scope}-{description}-{date}
- Example:
checkout-experimental-layout-2025-03,pricing-annual-discount-enabled-2026-01,onboarding-video-modal-shown-2026-02.
It's more characters, but it's searchable, scannable, and self-documenting.
Paired with TypeScript autocomplete (which you now have), good naming means you can find the right flag in seconds rather than hunting through a dashboard.
Know What Client-Side Flags Can and Can't Do
Feature flags evaluated in the browser are visible to users — anyone with DevTools can inspect the flag values your app receives. This is fine for UI experiments and gradual rollouts, but it means you should never use client-side feature flags to gate access to sensitive features or enforce permissions.
For anything security-sensitive — premium features, admin capabilities, access control — validate on the server. Client-side flags are for experience control, not authorization.
Getting Started
If you're using GrowthBook with React, here's the short path to type-safe flags:
- Generate your types using the GrowthBook CLI (
npx growthbook features generate-types) or through the MCP server in Cursor - Import and apply the types to your
useGrowthBookhook - Fix the errors TypeScript surfaces — treat each one as a bug caught before production
- Add loading state handling so users don't see flashes of the wrong UI
- Standardize your flag naming convention before your flag count grows
- Add the generation script to package.json and run it whenever your flags change
The type setup takes under 10 minutes. The bugs it prevents can take hours to diagnose after the fact — and the ones that reach users can take down conversions quietly for days before anyone notices.
GrowthBook has full documentation on TypeScript type generation for React and every other supported SDK. If you run into questions, the GrowthBook Slack community is active and helpful for anything experimentation-related.
