npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rozenite/feature-flags-plugin

v2.4.0

Published

Feature Flags Inspector for Rozenite.

Downloads

404

Readme

rozenite-banner

A Rozenite plugin for inspecting and overriding feature flags in React Native DevTools.

mit licence npm downloads Chat PRs Welcome

The Rozenite Feature Flags Plugin lets you list, inspect, and force-override feature flags on a running device from React Native DevTools — across a homegrown flag store, LaunchDarkly, and Statsig.

Read this first: Tier A vs. Tier B

Every adapter can list flags and show effective values. Whether setting an override just works, or needs a call-site change, depends on whether the plugin or the provider owns the override store:

  • Tier A — the provider owns the store. Statsig's LocalOverrideAdapter is a native local-override mechanism; the adapter just points at it. No call-site change. Overrides live exactly as long as that LocalOverrideAdapter instance does — durable if you construct it once at app start, ephemeral if you don't.
  • Tier B — the plugin owns the store. LaunchDarkly and the custom/local adapter have no such mechanism, so the plugin keeps an in-memory override map itself. Reading through it requires one changed line: for LaunchDarkly, pass the wrapped client (not the raw SDK client) to <LDProvider>. Overrides are in-memory and gone on app restart unless you wire persistence yourself via createFlagOverrides({ initial, onChange }).

This means "Reset all overrides" in the panel is durable on Tier A and ephemeral on Tier B — restarting the app after a Tier A reset keeps flags clean, while a Tier B reset only clears the current process's in-memory map. Neither is a bug; they're consequences of who owns the store. Each adapter section below states its lifetime in one line.

Installation

npm install @rozenite/feature-flags-plugin

Optional peers depending on the adapters you use:

npm install @launchdarkly/react-native-client-sdk
npm install @statsig/js-client @statsig/react-native-bindings @statsig/js-local-overrides

Usage

Custom / local adapter (Tier B — no provider, you declare the flags)

For a homegrown flag store, or as a placeholder before wiring a real provider. No call-site change beyond registering the adapter — flags are read straight from your own listFlags().

import {
  createCustomFlagsAdapter,
  useRozeniteFeatureFlagsPlugin,
} from '@rozenite/feature-flags-plugin';

// Module-level, like `storagePluginAdapters` in the playground app. The hook
// tracks `providers` by content, so a fresh array literal on every render
// works too -- hoisting just avoids rebuilding provider state for nothing.
const featureFlagsProviders = [
  createCustomFlagsAdapter({
    id: 'app',
    name: 'App flags',
    listFlags: () => flagStore.getAll(), // FeatureFlagInput[]: { key, value, type? }
  }),
];

useRozeniteFeatureFlagsPlugin({ providers: featureFlagsProviders });

setOverride throws for a key not present in listFlags() — nothing is written for a typo'd or unknown key.

Overrides are an in-memory Map, gone on app restart by default. Wire persistence with createFlagOverrides:

import { createCustomFlagsAdapter, createFlagOverrides } from '@rozenite/feature-flags-plugin';

const overrides = createFlagOverrides({
  initial: JSON.parse(storage.getString('flag-overrides') ?? '{}'),
  onChange: (all) => storage.set('flag-overrides', JSON.stringify(all)),
});

createCustomFlagsAdapter({ id: 'app', name: 'App flags', listFlags, overrides });

LaunchDarkly adapter (Tier B — wrapped client)

createLaunchDarklyFlagsAdapter returns a provider for the hook and a client you must pass to <LDProvider> in place of the raw SDK client — the one changed line. Every LD hook (useBoolVariation, useLDClient, ...) then reads through it automatically, since LD's hooks are a thin read off the context client.

import { ReactNativeLDClient, AutoEnvAttributes, LDProvider } from '@launchdarkly/react-native-client-sdk';
import {
  createLaunchDarklyFlagsAdapter,
  useRozeniteFeatureFlagsPlugin,
} from '@rozenite/feature-flags-plugin';

const rawClient = new ReactNativeLDClient(LD_MOBILE_KEY, AutoEnvAttributes.Enabled);
const { provider, client } = createLaunchDarklyFlagsAdapter({ client: rawClient });
const featureFlagsProviders = [provider];

useRozeniteFeatureFlagsPlugin({ providers: featureFlagsProviders });

<LDProvider client={client}>{/* ... */}</LDProvider>;

Overrides are in-memory, gone on app restart, unless you pass your own overrides (same createFlagOverrides option as the custom adapter). There is no refresh() — the LD client has no documented, version-stable way to force one; LD refreshes over its own streaming/polling connection instead. Anything holding a direct reference to the raw client (not the wrapped one) bypasses overrides.

Statsig adapter (Tier A — native override store)

You construct the StatsigClient and LocalOverrideAdapter yourself; the adapter only takes references to them. No call-site change. Override lifetime is whatever your LocalOverrideAdapter instance's lifetime is — this plugin doesn't manage it.

import { StatsigClient } from '@statsig/js-client';
import { LocalOverrideAdapter } from '@statsig/js-local-overrides';
import {
  createStatsigFlagsAdapter,
  useRozeniteFeatureFlagsPlugin,
} from '@rozenite/feature-flags-plugin';

const overrideAdapter = new LocalOverrideAdapter();
const client = new StatsigClient(STATSIG_CLIENT_KEY, { userID: 'user-123' }, { overrideAdapter });
await client.initializeAsync();

const featureFlagsProviders = [
  createStatsigFlagsAdapter({
    client,
    overrideAdapter,
    flags: [
      { key: 'new-onboarding' }, // boolean gate (default type)
      { key: 'checkout-copy', type: 'string' },
      { key: 'max-items', type: 'number' },
      { key: 'layout-config', type: 'json' },
    ],
  }),
];

useRozeniteFeatureFlagsPlugin({ providers: featureFlagsProviders });

Notes specific to this adapter:

  • Statsig has no client-side way to enumerate gates or dynamic configs, so you declare which flags exist up front via flags. setOverride/clearOverride/get-flag throw for a key that isn't declared.
  • A gate (type: 'boolean', the default) maps directly to checkGate/overrideGate.
  • Dynamic configs are Statsig's only non-gate primitive, and their value is a parameter map, not a single scalar. For string/number flags, this adapter reads and writes a single parameter named value within the config by convention (config.get('value', default) / overrideDynamicConfig(key, { value })) — this is a convention of this adapter, not a Statsig API. A json-typed flag reads/writes the config's full parameter map instead and avoids the convention entirely.
  • No refresh() — the closest SDK method (updateUserAsync) re-identifies the user rather than refetching specs for the current one, so this adapter doesn't invent a refresh convention around it.
  • "Reset all overrides" only clears the flags declared in flags. It does not call the override adapter's removeAllOverrides(), since a LocalOverrideAdapter instance may be shared with other debug tooling (or hold overrides for gates/configs this adapter doesn't know about) — those are left untouched.

Agent Tools (LLM Integration)

When this plugin is active, it registers agent tools under the @rozenite/feature-flags-plugin domain so coding agents can drive flags through Rozenite for Agents:

  • list-flags: list flags across providers (or a single one via providerId), including type, effective value, and override state.
  • get-flag: read a single flag by key.
  • override-flag: force a flag to a value — reproduce a bug report that only happens with a flag on.
  • clear-overrides: clear one override (pass key) or every override for a provider.

providerId is optional everywhere and resolves to the sole registered provider when there's only one.

Notes

  • A flag is { key, type: 'boolean' | 'string' | 'number' | 'json', value, overridden }value is always the effective, post-override value.
  • providers: FeatureFlagsProvider[] accepts more than one adapter at once (e.g. a homegrown flag store alongside LaunchDarkly during a migration). The panel only shows the multi-provider sidebar when more than one is registered.
  • There is no capabilities object on FeatureFlagsProvider — every Tier B provider can override everything it lists, and enumeration limits (like Statsig's) are handled by declaring flags up front instead.
  • refresh is optional on FeatureFlagsProvider and is only implemented where the provider has a real refetch primitive; neither the LaunchDarkly nor the Statsig adapter implements it (see each section above).

Made with ❤️ at Callstack

rozenite is an open source project and will always remain free to use. If you think it's cool, please star it 🌟.

Callstack is a group of React and React Native geeks, contact us at [email protected] if you need any help with these or just want to say hi!

Like the project? ⚛️ Join the team who does amazing stuff for clients and drives React Native Open Source! 🔥