ScaleBun
Skip to article

Flags, rollouts and experiments

react-nativeDeveloper

Runtime configuration delivered in one payload — feature flags, percentage rollouts, experiment variants, and remote values.

Updated Reviewed

Four related capabilities, all served by the same runtime config payload the SDK fetches at startup.

NamespaceQuestionReturns
configWhat is this value?Your typed value, or the fallback
flagsIs this on?boolean
rolloutsIs this on for this device?boolean
experimentsWhich variant does this device get?string | null

They resolve locally and synchronously#

Every one of these reads cached config. No network call, no promise — so they are safe to call during render:

TSX
function Checkout() {  if (ScaleBun.flags.isEnabled('new_checkout')) return <NewCheckout />;  return <LegacyCheckout />;}

Defaults before config arrives#

On a first launch there is no cached config yet. Every accessor returns a conservative default rather than throwing or blocking:

CallBefore config
flags.isEnabled(key)false
rollouts.isOn(key)false
experiments.variant(key)null
config.get(key, fallback)your fallback

Remote values#

For anything that is not a boolean:

TypeScript
const pageSize = ScaleBun.config.get('list_page_size', 20);const banner = ScaleBun.config.get('promo_banner_text', '');const limits = ScaleBun.config.get('upload_limits', { maxMb: 10 });

The fallback is required, and it is also the type source — pass the value you would ship if the config service never answered.

Force a refresh when you need one (after sign-in changes segment membership, for example):

TypeScript
await ScaleBun.config.refresh();

Rollouts vs flags#

A flag is a global switch. A rollout is a flag with a percentage: the same device consistently gets the same answer, so a user does not see a feature appear and disappear between launches. That consistency comes from bucketing on the device id, which is why clearUser() does not change a rollout assignment — identity and bucketing are separate.

TypeScript
if (ScaleBun.rollouts.isOn('beta_paywall')) {  // Stable for this device across launches.}

Experiments#

TypeScript
const variant = ScaleBun.experiments.variant('checkout_cta');
switch (variant) {  case 'treatment':    return <CtaB />;  case 'control':    return <CtaA />;  default:    // null = not enrolled, or config has not arrived. Show the control.    return <CtaA />;}

Handle null explicitly. Treating it as "treatment" enrols unassigned devices into your test arm and quietly invalidates the result.

To analyse an experiment you need the variant on the events you care about, so read it once and attach it:

TypeScript
ScaleBun.track('checkout_completed', {  variant: ScaleBun.experiments.variant('checkout_cta') ?? 'unassigned',});

Entitlements ride the same payload#

The config response also carries entitlements — which capabilities your plan allows. The SDK applies them before registering a collector, so an unentitled capability produces no traffic at all. See Architecture.

Flags, rollouts and experiments · Engagement · React Native… · ScaleBun