Feature management for Expo apps.

Ship every feature dark, release it to the right people at the right pace, and pull it back in one click. Gates runs flags, rollouts, audiences and kill switches from a snapshot the phone already has, so your app renders first and syncs later.

npx expo install @gates/expo

App.tsx
import { GatesProvider, useFlag, Switch } from "@gates/expo";

export default function Root() {
  const { user } = useAuth();
  return (
    <GatesProvider
      projectKey="ek_…"
      environment="production"
      user={user && { id: user.id }}
    >
      <App />
    </GatesProvider>
  );
}

function Checkout() {
  const oneTap = useFlag("checkout-v2"); // local read, never fetches
  return (
    <Switch name="payments" fallback={<PaymentsDown />}>
      {oneTap ? <OneTapCheckout /> : <ClassicCheckout />}
    </Switch>
  );
}
gates.explain("checkout-v2")
// { value: true, reason: "rollout_match", bucket: 1873,
//   revision: 42, source: "cache" }

Built for how Expo apps actually ship.

Store builds, OTA updates and runtime versions are first-class in Gates, not custom attributes you wire up yourself. Managing a feature means managing every build it is in.

Evaluated on the device

The SDK reads the last snapshot it saved. useFlag is synchronous, never fetches and never throws. Airplane mode changes nothing.

readAsyncStorage → snapshotsyncstartup · foreground · identifywaitnever

Targets what Expo already knows

Rules match on the context expo-application and expo-updatesexpose. No native module to add, and appVersion compares as a real version, never as a string.

platform= iosappVersion>= 2.4.0runtimeVersion= 2.4.0updateId= 8f3a21c…

Rollouts that stay put

A rollout seed is minted once per rule and never rotated. Someone in at 25% is still in at 50%, on every one of their devices.

hash(flagKey + seed + identity) → 0…999925% admits bucket < 2500

A key that can only read

The environment key you ship reads configuration and reports exposure. It cannot create, edit or delete a flag, change a rollout or see anything else in the project.

ek_…sync · heartbeat · eventsadmindashboard sign-in only

From dark launch to full release.

Four steps between the dashboard and the device, and the phone never blocks on any of them.

  1. Create a flag

    Name it, pick an environment, keep it off. The SDK treats an unknown flag as off, so shipping the code first is safe.

  2. Roll it out

    Add a rule: platform, version, update, an audience from People, or a percentage. Every change bumps the environment revision.

  3. Devices evaluate offline

    Phones fetch the new snapshot on their next sync and store it. From then on every read is local and deterministic.

  4. Kill it in one click

    Disable the flag. The next sync turns it off on every build and update that ships it; nothing waits on a release.

One switch. Every build that has the flag.

A flag can be in a store build from March and an OTA update from this morning at the same time. Gates records which versions have evaluated each flag, so the kill switch shows what it reaches before you press it.

  • Lists the store builds and OTA updates that ship the flag
  • Off is project-wide: development, preview and production at once
  • Phones converge on their next sync; the app keeps rendering throughout
  • <Switch> fails open, so a feature that already shipped stays up until you say otherwise
The Versions & cohorts drawer for the checkout-v2 kill switch in the Gates dashboard: which store builds and OTA updates ship the flag, how many active installations each has, and whether it was seen on them.
The Audiences screen in the Gates dashboard: named segments such as iOS 2.4+ and Pro users, each with its rule and the number of matched users.

Target people, not just installs.

Pass a user to the provider and Gates keeps a profile: traits you send, the devices they use, the versions they are on. Group them into audiences and point a rule at the audience.

user.plan= proplatform= iosappVersion>= 2.4.0audience= pro-ios

Identity is user.id when someone is signed in and the installation id when nobody is, so a rollout follows a person across their phones.

Free to start. Priced on devices, not seats.

Every workspace gets 14 days of Pro, no card, then stays free for one project. Paid plans meter one thing: devices that synced this month. Pricing details

Free

One app on the side. No card, no clock.

$0/mo

Free for as long as you like.

  • 1 project with three environments
  • 5 flags and audiences, combined
  • 1K active installations / month
  • 7 days telemetry retention
  • Unlimited seats
  • Kill switches and rollouts
  • Community support on GitHub
Start free

Scale

A large install base, and the teams around it.

$159/mo

Billed yearly, or $199 month to month.

  • Unlimited projects and flags
  • 250K active installations / month
  • +250K for $100 / month beyond that
  • 1 year telemetry retention
  • Everything in Pro
  • SSO / SAML
  • EU or US data residency
  • Shared Slack channel
Start 14-day trial

Questions people ask first.

Does my app wait on Gates before rendering?

No. The provider renders its children immediately. Until the local snapshot has been read, every flag returns the fallback you gave it; after that, reads come from storage. The network is only used to fetch a newer snapshot in the background.

What happens when a phone is offline, or the API is down?

Flags keep evaluating from the last snapshot the device saved. A failed or malformed sync sets lastError and keeps the previous snapshot; it never replaces good state with bad.

Is it safe to ship the key inside the app?

Yes. The environment key can read configuration and send heartbeats and exposure events. It cannot create, edit or delete flags, change rollouts or read anything else. Changing configuration requires dashboard sign-in.

Does Gates run A/B tests with statistics?

No. Flags are boolean, so an <Experiment> has exactly two arms and the rollout percentage sets the split. Gates records exposure and adoption so you can see what a flag reached, and leaves significance to your analytics tool.

What does it cost?

Gates Cloud is free for one project and five flags, then $39 or $199 a month billed yearly ($49 / $199 monthly), priced on active installations: devices that synced at least once in the month. Seats are always unlimited. Every workspace starts with 14 days of Pro, no card needed. Self-hosting is free and unlimited.

Which platforms are supported?

Expo apps on iOS and Android, through the @gates/expo package. There is no native, Flutter or server SDK today; the core client has no React or Expo imports, so it also runs in Jest and vitest.

Put a gate in front of your next feature.

Install the SDK, wrap your app, ship the code dark. Release it when you are ready, to whoever you choose.