@geenius/feature-flags
v0.17.0
Published
Geenius Feature Flags — Runtime feature toggles, rollout controls, and experiment tooling
Maintainers
Readme
@geenius/feature-flags
Runtime feature toggles, gradual rollouts, A/B experiments, developer overrides, and provider-backed flag persistence for Geenius applications.
Status:
0.16.1now ships shared evaluation, the four launch providers (convex,neon,cloudflareKV,memory), and the five in-scope UI variants (react,react-css,solidjs,solidjs-css,react-native). The remaining 10 UI library variants are deferred by the current run scope. See.docs/PRDS/packages/PACKAGE_FEATURE_FLAGS_PRD.mdfor the full contract and.docs/DOCS/PACKAGES/FEATURE_FLAGS.mdfor the consumer-facing reference.
What it does
- Deterministic flag evaluation: same
(identity, flagKey)pair always resolves to the same bucket. - Three flag types:
boolean,string(variants),number(values). - Rollout strategies: percentage hash bucketing, user/org targeting rules, scheduled launches.
- Local developer overrides via
useDevOverrides()/createDevOverrides()— stored undergeenius-ff-dev-overrides, never written to a remote provider. - A/B experiments with stable variant assignment and exposure logging.
- Per-provider flag CRUD, target-rule editing, log retrieval, and aggregate stats through one shared contract.
Installation
pnpm add @geenius/feature-flagsThen install the peers for the variant you render with:
| Variant subpath | Peer dependencies |
| ---------------------------------------- | ------------------------------------------------- |
| @geenius/feature-flags/react | react, react-dom |
| @geenius/feature-flags/react-css | react, react-dom |
| @geenius/feature-flags/solidjs | solid-js |
| @geenius/feature-flags/solidjs-css | solid-js |
| @geenius/feature-flags/convex | convex |
| @geenius/feature-flags/neon | @neondatabase/serverless |
| @geenius/feature-flags/cloudflareKV | Cloudflare Workers KV runtime / workers types |
| @geenius/feature-flags/memory | none |
| @geenius/feature-flags/react-native | react, react-native |
The remaining canonical UI variants (react-shadcn, react-ant, react-chakra, react-mui, react-mantine, react-heroui, react-daisyui, solidjs-ark, solidjs-kobalte, solidjs-solidui) are deferred by this run scope.
Quickstart — React + Tailwind
import type { JSX } from "react";
import { useFeatureFlags } from "@geenius/feature-flags/react";
import "@geenius/feature-flags/react/styles.css";
import type { EvaluationContext, FeatureFlag } from "@geenius/feature-flags";
const flags: FeatureFlag[] = [
{
id: "flag_new_dashboard",
key: "new-dashboard",
name: "New dashboard",
type: "boolean",
defaultValue: false,
enabled: true,
rolloutStrategy: "percentage",
rolloutPercentage: 25,
tags: ["dashboard"],
createdAt: "2026-04-01T00:00:00.000Z",
updatedAt: "2026-04-01T00:00:00.000Z",
},
];
const context: EvaluationContext = { userId: "user_123", plan: "pro" };
export function DashboardGate(): JSX.Element {
const runtime = useFeatureFlags(flags, context);
return runtime.isEnabled("new-dashboard") ? <NewDashboard /> : <CurrentDashboard />;
}useFeatureFlags(flags, ctx, overrides?) returns { isEnabled, getValue, getVariant, getEvaluation, evaluations, flags, isLoading }. Wrap a subtree in <FeatureFlagsProvider flags ctx overrides> to read the same runtime through useFeatureFlagsContext().
Quickstart — React + vanilla CSS (no Tailwind)
import { DevOverridePanel, useDevOverrides } from "@geenius/feature-flags/react-css";
import "@geenius/feature-flags/react-css/styles.css";Selectors use the package-scoped BEM namespace .gn-feature-flags-*, with elements and modifiers following .gn-feature-flags-<block>__<element>--<modifier>. CSS custom properties come from @geenius/tokens through the shared --gn-* namespace, including --gn-success, --gn-warning, and --gn-danger. The parent package and CSS sub-packages declare sideEffects: ["**/*.css"] so stylesheet imports survive consumer bundling.
Quickstart — SolidJS
import { Show } from "solid-js";
import type { EvaluationContext, FeatureFlag } from "@geenius/feature-flags";
import { createFeatureFlags } from "@geenius/feature-flags/solidjs";
import "@geenius/feature-flags/solidjs/styles.css";
export function DashboardGate(props: {
flags: () => FeatureFlag[] | undefined;
context: () => EvaluationContext;
}) {
const runtime = createFeatureFlags(props.flags, props.context);
return (
<Show when={runtime.isEnabled("new-dashboard")} fallback={<CurrentDashboard />}>
<NewDashboard />
</Show>
);
}Stylesheet subpaths
@geenius/feature-flags/react/styles.cssand@geenius/feature-flags/solidjs/styles.cssexport the Tailwind reference layer used by the default React and SolidJS variants.@geenius/feature-flags/react-css/styles.cssand@geenius/feature-flags/solidjs-css/styles.cssexport the standalone vanilla CSS layer for hosts that do not use Tailwind.
Import the matching stylesheet once from the app shell before rendering package UI.
Provider setup
Convex
import {
componentDefinition,
featureFlagTables,
mutations,
queries,
schema,
} from "@geenius/feature-flags/convex";Convex carries feature_flags, flag_evaluations_log, and dev_overrides tables, plus queries/mutations for CRUD, rollout updates, target rules, evaluation logs, stats, and developer overrides.
Neon
import {
createNeonFeatureFlagsStore,
featureFlagsMigrations,
runFeatureFlagsMigrations,
} from "@geenius/feature-flags/neon";Neon provides the SQL-backed store for serverless Postgres deployments, including append-only migrations, idempotent migration application, CRUD, evaluation logging, stats, and developer overrides.
Cloudflare KV
import {
createCloudflareKVFeatureFlagsStore,
createMemoryKVNamespace,
} from "@geenius/feature-flags/cloudflareKV";Cloudflare KV provides edge-local feature reads, TTL-backed flag snapshots, and key/value persistence. createMemoryKVNamespace() is available for local tests and Storybook fixtures.
Memory
import { createMemoryFeatureFlagsStore } from "@geenius/feature-flags/memory";The memory provider is process-local and ephemeral. Use it for unit tests, Storybook fixtures, local demos, and lightweight development harnesses.
Imports cheatsheet
import {
evaluateFlag,
evaluateAllFlags,
configureFeatureFlags,
defineFeatureFlagsConfig,
getFeatureFlagsConfig,
mergeFeatureFlagsConfig,
resetFeatureFlagsConfig,
buildFeatureFlagsSeoMeta,
DEV_OVERRIDES_STORAGE_KEY,
RULE_OPERATORS,
RULE_ATTRIBUTES,
FLAG_TAGS,
formatRelativeTime,
getFeatureFlagAttributeLabel,
getFeatureFlagOperatorLabel,
getFeatureFlagReasonLabel,
getFeatureFlagStatusLabel,
getFeatureFlagTypeLabel,
pluralizeFeatureFlags,
translateFeatureFlags,
FeatureFlagsError,
FeatureFlagNotFoundError,
} from "@geenius/feature-flags";
import type {
FeatureFlagParams,
FeatureFlagTranslator,
} from "@geenius/feature-flags";
import {
FeatureFlagsProvider,
useFeatureFlagsContext,
useFeatureFlags,
useDevOverrides,
useFeatureFlagsAdmin,
useFlagStats,
ABTestCard,
DevOverridePanel,
FlagStatusBadge,
FlagToggle,
RolloutSlider,
TargetRulesEditor,
FeatureFlagsPage,
FlagDetailPage,
DevOverridePage,
} from "@geenius/feature-flags/react";const config = defineFeatureFlagsConfig({ provider: "memory", devMode: true });
configureFeatureFlags(config);
const activeConfig = mergeFeatureFlagsConfig(getFeatureFlagsConfig(), {
cacheMs: 60_000,
});
const seo = buildFeatureFlagsSeoMeta({
title: "Feature Flags",
description: "Manage rollouts and developer overrides.",
canonicalUrl: "/admin/feature-flags",
});
resetFeatureFlagsConfig();Localized label helpers are also root exports:
const params: FeatureFlagParams = { count: 2 };
const translator: FeatureFlagTranslator = (key, values) =>
translateFeatureFlags(key, values);
const statusLabel = getFeatureFlagStatusLabel("enabled", translator);
const typeLabel = getFeatureFlagTypeLabel("boolean", translator);
const reasonLabel = getFeatureFlagReasonLabel("rollout", translator);
const attributeLabel = getFeatureFlagAttributeLabel("plan", translator);
const operatorLabel = getFeatureFlagOperatorLabel("not-in", translator);
const overrideCountLabel = pluralizeFeatureFlags(
"featureFlags.overrides.active",
2,
params,
);The root . export carries the entire framework-agnostic surface (evaluator, types, errors, helpers). There is no @geenius/feature-flags/shared subpath — that workspace package is private plumbing.
Storybook
The repository includes per-variant Storybook v10 apps. As of 0.16.1 the active hosts are:
apps/storybook-react/apps/storybook-react-css/apps/storybook-solidjs/apps/storybook-solidjs-css/apps/storybook-react-native/
The four web apps build with the @geenius/storybook preset/manager/preview helpers and run the same Welcome, Tokens, and FeatureFlags story matrix so visual and behavioral parity stays reviewable across frameworks and styling variants. The React Native app follows the native Storybook shape and ships the same feature-flag story coverage. The remaining 10 canonical Storybook apps land with the V1 expansion wave.
Package contract
@geenius/feature-flagsexposes the framework-agnostic evaluation engine, shared types, helpers, and typed errors.@geenius/feature-flags/reactand@geenius/feature-flags/solidjsexpose Tailwind-oriented UI and reactive bindings plus./react/styles.cssand./solidjs/styles.cssstylesheet subpaths.@geenius/feature-flags/react-cssand@geenius/feature-flags/solidjs-cssexpose standalone vanilla CSS implementations plus./react-css/styles.cssand./solidjs-css/styles.cssstylesheet subpaths.@geenius/feature-flags/react-nativeexposes native feature-flag components and bindings withaccessibilityLabelandtestIDselectors.@geenius/feature-flags/convexexposes the Convex schema, queries, mutations, and validators.@geenius/feature-flags/neon,@geenius/feature-flags/cloudflareKV, and@geenius/feature-flags/memoryexpose provider stores for SQL, edge KV, and in-process usage.- Variant parity is measured at the public export map, framework subpath barrels, and generated declarations. Package-internal fixtures, storage adapters, class utilities, motion shims, and SEO helpers are not portable imports unless a public subpath exports them.
Tarball-purity regex (parent-only publish):
^package/(packages/.+/dist/.+|(README|CHANGELOG|SECURITY)\.md|LICENSE|package\.json)$Developing
variants.json is the source of truth for every package variant, provider, Storybook app, Playwright project, coverage threshold, and bundle budget. Add or change a variant there first, then add the matching package source and tests.
pnpm run variants:summary # list canonical axis vs. on-disk
pnpm run lint # Biome on packages, configs, tests
pnpm run lint:apps # Biome on Storybook + harness apps
pnpm run type-check # tsconfig.typecheck.json fan-out
pnpm run test:unit # vitest per sub-package + parity
pnpm run test:gauntlet # PR-blocking gates
pnpm run test:all # nightly: + Storybook, DB conformance, e2e, axe, visual, perf
pnpm run test:mutation # weekly Stryker runPre-commit should run Biome on changed files via lint-staged or the host repo's hook runner — the package gauntlet does not install hooks.
License
Commercial package. See LICENSE for the Geenius commercial terms.
