Skip to content

FRD: Feature Flags Provider

Document Summary

FieldDetails
Feature NameFeature Flags Provider
StatusDraft
OwnerDavid Holmes
ContributorsEngineering
Target Releasev2.0.0
Related LinksADR-051 (Provider Pattern), ADR-027 (Default Tech Stack — feature flags not yet designated; GrowthBook recommended), ADR-014 (Open Source First)
Last Updated2026-06-02
Open Source Libraries@growthbook/growthbook
DocumentationGrowthBook JS Docs · React SDK · SSR / Next.js · Feature Flag API

This FRD ships @dmwd-io/feature-flags as an optional package that re-exports @growthbook/growthbook as the platform standard for feature flags and A/B experiments via GrowthBook. The value is standards and drift prevention — a single designated entry point with documented env var conventions — not a custom abstraction layer.


Introduction

Overview

Every app built on the design system needs rollout controls — gradual rollouts, A/B experiments, kill switches, and environment-specific toggles. Currently each app either hard-codes if (process.env.FEATURE_X) checks, imports a vendor SDK (LaunchDarkly, Flagsmith, Unleash) directly, or skips flags entirely and deploys everything at once. Per ADR-014 (Open Source First), this FRD designates @growthbook/growthbook as the community library for feature flags and re-exports it through @dmwd-io/feature-flags. Apps import from the platform package, and the platform documents GROWTHBOOK_CLIENT_KEY and GROWTHBOOK_API_HOST as the standard env var conventions. No custom provider interface is built — GrowthBook’s own API is the interface.

Goals

  • Designate @growthbook/growthbook as the platform standard for feature flags and A/B experiments (per ADR-014).
  • Ship @dmwd-io/feature-flags as a thin re-export of @growthbook/growthbook to standardize the import path and prevent drift.
  • Document GROWTHBOOK_CLIENT_KEY and GROWTHBOOK_API_HOST as the canonical platform env var names.
  • Ensure SSR-safe operation — GrowthBook supports server-side flag loading natively.
  • Document the need to add GrowthBook to ADR-027 as the designated feature flag solution.

Non-Goals

  • Building a custom provider interface or adapter layer — the library’s own API is the interface (per ADR-014).
  • LaunchDarkly, Flagsmith, or Unleash adapters (SaaS options; prefer GrowthBook self-hosted per platform philosophy).
  • Implementing a flag management UI or admin dashboard.
  • Building a targeting rules engine (percentage rollouts, user segment matching) — that is the vendor’s responsibility.
  • Real-time flag streaming/polling — GrowthBook handles transport natively.
  • Analytics integration for experiment tracking (see FRD: Analytics Provider Wrapper).

Scope

In Scope

AreaDescription
@dmwd-io/feature-flags packageThin re-export of @growthbook/growthbook; single canonical import path for all platform apps
Env var conventionsGROWTHBOOK_CLIENT_KEY and GROWTHBOOK_API_HOST documented as platform-standard names
SSR bootstrapDocumented pattern for server-side flag loading using GrowthBook’s native API
Storybook docsExample usage, SSR bootstrap flow, and multi-variant experiment patterns
ADR-027 supplementRecommendation to formalize GrowthBook as the designated feature flag solution

Out of Scope

AreaReason
Custom provider interfaceADR-014: library API is the interface
Test adapter implementationGrowthBook provides its own testing utilities
Factory function wrapperAdds indirection without value; apps use GrowthBook’s init directly
Custom React hooks/componentsGrowthBook’s @growthbook/growthbook-react package covers this
Flag management UIVendor-hosted or separate admin tooling
Targeting rules engineVendor responsibility
Experiment analyticsCovered by FRD: Analytics Provider Wrapper

Users and Pain Points

User Groups

UserDescriptionNeeds
App developersEngineers shipping features incrementallyTyped flag checks that work in SSR and client-side rendering
QA engineersTesters validating feature behavior under different flagsDeterministic test adapter to set any flag combination
Product managersStakeholders controlling rolloutA standard contract that connects to any vendor’s dashboard

Pain Points

UserPain PointImpact
App developersFeature flags are process.env checks with no type safety — removed flags cause silent bugsStale flag checks persist in code; runtime errors in production
App developersNo SSR support — flag evaluation on the client causes layout flickerPoor user experience during hydration
QA engineersCannot set flag values in Storybook — stories always show the default stateFlag-gated features are not demoed or tested in isolation
App developersVendor SDK imports scatter across componentsVendor lock-in; migration requires touching every component

Definitions

TermDefinition
Feature flagA named toggle that controls whether a feature is active for a given context
Flag valueThe resolved value of a flag — boolean for on/off, string for multi-variant experiments
Evaluation contextUser and environment attributes used for targeting (user ID, tenant, environment, etc.)
BootstrapPre-loading flag values on the server so the client renders with correct values immediately
ExperimentA multi-variant flag where users are assigned to different variants (e.g. “control”, “variant-a”, “variant-b”)
Kill switchA flag used to disable a feature instantly without a deployment
Thin re-exportA package that imports and re-exports a community library, adding only platform conventions

Current State

Existing Behavior

No feature flag abstraction exists in the design system. Apps use one of three approaches: (1) process.env.NEXT_PUBLIC_FEATURE_X === "true" checks, (2) direct LaunchDarkly/Flagsmith SDK imports in components, or (3) no flags at all — features ship as all-or-nothing deployments.

Current Limitations

  • No shared TypeScript types for flags or evaluation context.
  • No SSR-safe flag evaluation — client-only checks cause hydration mismatches.
  • No test adapter — flag-dependent code paths are tested by setting environment variables or mocking vendor SDKs.
  • No React helpers — each app writes its own useFlag hook wrapping a vendor SDK.
  • Flag names are untyped strings — typos and removed flags are not caught at compile time.

Existing Workarounds

  • Apps wrap vendor SDK calls in custom hooks with ad-hoc caching.
  • Storybook stories hard-code feature states with args instead of using a flag provider.
  • Environment variable checks are scattered throughout components.

Proposed Solution

Summary

Per ADR-014 (Open Source First), @dmwd-io/feature-flags re-exports @growthbook/growthbook as the platform standard. No custom interface is built — the library’s API is the interface. Apps import from the platform package:

import { GrowthBook } from '@dmwd-io/feature-flags';

The package documents two platform-standard env var names:

  • GROWTHBOOK_CLIENT_KEY — the SDK client key for the GrowthBook API
  • GROWTHBOOK_API_HOST — the self-hosted GrowthBook instance URL (defaults to https://cdn.growthbook.io)

GrowthBook natively supports SSR bootstrap, multi-variant experiments, typed flag definitions via its SDK, and React helpers via @growthbook/growthbook-react. The platform package can optionally re-export from @growthbook/growthbook-react as well to provide a single import path.

User Experience

End users see fewer flickers during page load (flags resolved server-side via GrowthBook’s native bootstrap) and more consistent feature behavior (flags evaluated uniformly across the platform).

Developer Experience

Developers initialize GrowthBook once using platform-standard env vars and get the full GrowthBook feature set — typed attributes, multi-variant experiments, SSR support — without any custom abstraction to learn:

import { GrowthBook } from '@dmwd-io/feature-flags';
const gb = new GrowthBook({
apiHost: process.env.GROWTHBOOK_API_HOST,
clientKey: process.env.GROWTHBOOK_CLIENT_KEY,
trackingCallback: (experiment, result) => {
// wire to analytics
},
});
await gb.loadFeatures();

Flag names autocomplete via GrowthBook’s typed feature definitions. Storybook stories use GrowthBook’s built-in test utilities to set flag values deterministically.


7a. ADR-027 Gap

Feature flags are not yet designated in ADR-027. This FRD recommends opening an ADR-027 supplement to add GrowthBook as the default feature flag solution. GrowthBook is open-source, self-hosted, supports A/B testing, and aligns with the platform’s preference for self-hosted tools (PostHog, GlitchTip, Meilisearch).


Requirements

IDRequirementPriorityNotes
FR-001Publish @dmwd-io/feature-flags package re-exporting @growthbook/growthbookMust-
FR-002Document GROWTHBOOK_CLIENT_KEY and GROWTHBOOK_API_HOST as platform-standard env varsMust-
FR-003Document SSR bootstrap pattern using GrowthBook’s native loadFeaturesMust-
FR-004Optionally re-export @growthbook/growthbook-react for React hook and component supportShould-
FR-005Storybook docs page with usage examples, SSR flow, and multi-variant experiment patternMust-
FR-006Open ADR-027 supplement designating GrowthBook as the platform feature flag standardMust-

Priority Definitions

PriorityMeaning
MustRequired for this feature to ship.
ShouldImportant, but can be deferred if needed.
CouldNice to have. Not required for initial release.

Functional Requirements

IDRequirementUser BenefitPriority
FUNC-001import { GrowthBook } from '@dmwd-io/feature-flags' works in any platform appSingle canonical import path, no vendor sprawlMust
FUNC-002GROWTHBOOK_CLIENT_KEY and GROWTHBOOK_API_HOST are documented as the platform env var namesConsistent configuration across all appsMust
FUNC-003SSR bootstrap pattern is documented with a working code exampleNo client-side flicker for flag-gated featuresMust
FUNC-004React hook and component (useFeature, IfFeatureEnabled) available via the platform packageDeclarative feature gating in React componentsShould
FUNC-005Multi-variant experiment pattern is documented with a code exampleTeams can run A/B tests without custom toolingShould

Non-Functional Requirements

IDRequirementCategoryPriority
NFR-001@dmwd-io/feature-flags adds no runtime code beyond re-exportsPerformanceMust
NFR-002No custom provider interface or adapter layer is introducedMaintainabilityMust
NFR-003@growthbook/growthbook is listed as a peer dependency, not a bundled dependencyPerformanceMust
NFR-004Package exports are tree-shakeablePerformanceShould
NFR-005SSR bootstrap does not leak flag values into client-visible HTML beyond the serialized stateSecurityShould

API / Interface Requirements

Public API

The public API is GrowthBook’s own API. The platform package re-exports it wholesale. Key exports available via @dmwd-io/feature-flags:

NameTypeDescription
GrowthBookclassCore SDK class for flag evaluation and experiment assignment
useFeaturehook (react)Returns the feature result for a given flag name
IfFeatureEnabledcomponent (react)Renders children when a feature flag is enabled
FeaturesReadycomponent (react)Renders children after features are loaded from the API

Example Usage

// Initialize (e.g. in app entry point or server loader)
import { GrowthBook } from '@dmwd-io/feature-flags';
const gb = new GrowthBook({
apiHost: process.env.GROWTHBOOK_API_HOST,
clientKey: process.env.GROWTHBOOK_CLIENT_KEY,
});
await gb.loadFeatures();
// In a React component
import { useFeature, IfFeatureEnabled } from '@dmwd-io/feature-flags';
function Dashboard() {
const feature = useFeature("new-dashboard");
return feature.on ? <NewDashboard /> : <LegacyDashboard />;
}
// Declarative gating
<IfFeatureEnabled id="new-dashboard">
<NewDashboard />
</IfFeatureEnabled>

API Notes

  • GrowthBook’s useFeature returns an object with .on, .off, .value, and .experiment — richer than a simple boolean.
  • SSR bootstrap uses gb.setFeatures(serializedFlags) to hydrate without a client-side API call.
  • GrowthBook supports typed feature definitions via its SDK — see the GrowthBook docs for the TypeScript code generation workflow.

Accessibility Requirements

IDRequirementNotes
A11Y-001IfFeatureEnabled must not produce empty DOM nodes that confuse screen readersWhen flag is off, render null, not an empty wrapper
A11Y-002Flag-gated content must not cause focus loss when flags changeIf visible content is removed by a flag change, focus should move to a sensible target

Checklist

  • Keyboard support is defined. (IfFeatureEnabled is transparent to keyboard navigation)
  • Focus behavior is defined. (Flag changes should not orphan focus)
  • Screen reader behavior is defined. (IfFeatureEnabled renders null when off, no phantom elements)
  • Color contrast requirements are met. (N/A — no visual output)
  • Reduced motion behavior is considered. (N/A — no animation)
  • Semantic HTML expectations are documented. (IfFeatureEnabled renders no wrapper element)
  • ARIA usage is defined only where needed. (N/A)

Content and Documentation Requirements

IDRequirementLocationPriority
DOC-001Storybook docs page explaining the feature flags pattern and @dmwd-io/feature-flags packageStorybookMust
DOC-002Inline JSDoc on any platform-specific utilities or conventions added to the packageSource codeMust
DOC-003Example: SSR bootstrap flow using GrowthBook’s native setFeaturesStorybookMust
DOC-004Example: Storybook story showing a component in both flag-on and flag-off statesStorybookMust
DOC-005Example: multi-variant experiment using useFeature with variant valuesStorybookShould

Dependencies

DependencyTypeOwnerStatusNotes
@growthbook/growthbookRuntime (peer)GrowthBook OSSReadyCommunity library designated per ADR-014
@growthbook/growthbook-reactRuntime (peer, optional)GrowthBook OSSReadyReact hooks and components
ADR-014 (Open Source First)ArchitectureEngineeringReadyGoverns the thin re-export approach
ADR-051 provider patternArchitectureEngineeringReadyGeneral provider conventions
React (peer dependency)RuntimeReact teamReadyRequired for React helper re-exports

Risks and Tradeoffs

Risk / TradeoffImpactMitigation
Adopting GrowthBook’s API directly means platform apps are coupled to GrowthBook’s interfaceMigration to another tool requires updating all call sitesGrowthBook is open-source and self-hosted; per ADR-014 this is the acceptable tradeoff for avoiding custom abstraction maintenance
GrowthBook SDK updates may introduce breaking changesApps need to update when major versions shipPin @growthbook/growthbook to a tested version in @dmwd-io/feature-flags; update deliberately
No custom interface means no compile-time enforcement of platform-specific conventions (PII sanitization, event naming)Teams may bypass conventionsDocument conventions clearly; enforce via code review and linting rules
Real-time flag streaming requires GrowthBook server-sent events supportFlag changes require configuration of GrowthBook’s streaming endpointDocument streaming setup; it is native to GrowthBook and does not require custom code

Open Questions

IDQuestionOwnerStatusResolution
Q-001Should @dmwd-io/feature-flags also re-export @growthbook/growthbook-react, or keep it as a separate import?David HolmesOpen
Q-002Should the platform provide a pre-configured createGrowthBook() helper that reads GROWTHBOOK_CLIENT_KEY and GROWTHBOOK_API_HOST automatically?David HolmesOpen
Q-003Should flag evaluation context include request headers for server-side targeting (e.g. geo, device)?David HolmesOpen
Q-004Should @dmwd-io/feature-flags export a platform-standard GrowthBookProvider wrapper for React apps?David HolmesOpen

Acceptance Criteria

IDCriteriaRelated Requirement
AC-001import { GrowthBook } from '@dmwd-io/feature-flags' resolves correctly in a TypeScript projectFR-001
AC-002GROWTHBOOK_CLIENT_KEY and GROWTHBOOK_API_HOST are documented in the package README and Storybook pageFR-002
AC-003SSR bootstrap example compiles and runs without errorsFR-003
AC-004pnpm typecheck passes on the packageNFR-002
AC-005No custom provider interface or adapter is present in the package sourceNFR-002
AC-006@growthbook/growthbook appears as a peer dependency, not a bundled dependency, in package.jsonNFR-003

LLM Handoff Instructions

Expected LLM Behavior

  • Create packages/feature-flags/ directory (or follow the existing packages directory structure in the repo).
  • Create packages/feature-flags/package.json with @growthbook/growthbook and @growthbook/growthbook-react as peer dependencies.
  • Create packages/feature-flags/src/index.ts that re-exports everything from @growthbook/growthbook and (optionally) @growthbook/growthbook-react.
  • Document GROWTHBOOK_CLIENT_KEY and GROWTHBOOK_API_HOST as the platform-standard env var names in the package README or a docs/ file.
  • Run pnpm typecheck to confirm the package compiles cleanly.

LLM Should Not

  • Build a custom FeatureFlagsProvider interface, adapter, or factory function.
  • Import and re-wrap GrowthBook’s API in custom hooks or components.
  • Add runtime dependencies beyond @growthbook/growthbook and @growthbook/growthbook-react (peers).
  • Implement targeting rules, percentage rollouts, or segment matching.
  • Add a custom test adapter — GrowthBook provides test utilities natively.

Decision Log

DateDecisionReasonOwner
2026-05-26Synchronous evaluate() after bootstrap()Prevents async rendering waterfalls and hydration mismatchesDavid Holmes
2026-05-26Generic TFlags parameter for typed flag namesCompile-time safety catches typos and stale flag referencesDavid Holmes
2026-05-26React helpers as optional exports, not requiredKeeps the core interface framework-agnosticDavid Holmes
2026-05-26GrowthBook recommended as the default feature flags adapterNot yet in ADR-027; open-source self-hosted fits platform philosophy. ADR-027 supplement needed to formalize.David Holmes
2026-06-02Reframed per ADR-014 (Open Source First): adopt @growthbook/growthbook as thin re-export rather than building a custom provider interface. Library API is the interface.ADR-014 establishes that community libraries should be adopted directly; custom abstractions add maintenance cost without proportional value.David Holmes

Document History

DateAuthorChange
2026-05-26David HolmesInitial draft
2026-06-02David HolmesReframed per ADR-014: ship as thin re-export of @growthbook/growthbook, drop custom interface.