Skip to content

Rollout & Scheduling

Two ways for a flag's value to be decided automatically — by population percentage, or by date — rather than by a manual override.

Percentage rollout

Instead of a plain boolean or string, a flag in the flags option can be a { value, rollout } object:

ts
app.use(FeatureToggles, {
  flags: {
    newSearch: { value: true, rollout: 0.2 }, // 20% of users
  },
  userId: currentUser.id, // stable per-user identifier
})

rollout is a number between 0 and 1. Each user is bucketed deterministically: the flag name and userId (falling back to 'anonymous' if userId isn't set) are hashed together, and the user gets value if the hash falls under the rollout threshold — otherwise the flag resolves to false, never to defaultValue. The same userId always lands in the same bucket, so a rollout never flickers for a given user across reloads, and increasing the percentage later only ever adds users to the enabled group, it never removes any.

ts
const { getRollout } = useFeatureProvider()

getRollout('newSearch') // → 0.2

Rollout resolution happens once, when the provider is created — changing userId later does not re-bucket existing flags reactively.

Scheduling

A flag can be forced off outside a configured date window, independent of any manual override:

ts
app.use(FeatureToggles, {
  flags: { holidayBanner: true },
  schedule: {
    holidayBanner: { from: '2025-12-01', to: '2025-12-31' },
  },
})
ts
interface FlagSchedule {
  from?: string // ISO date — flag is forced off before this date
  to?: string // ISO date — flag is forced off after this date
}

Both from and to are optional — omit from for an "active until" window, or to for an "active from" window. The check re-evaluates every minute while the page is open, so a scheduled flag flips on/off live without a reload.

Scheduling only forces a flag to false when it's otherwise resolved to something else — it never overrides an active URL override or a runtime setFlag() call, so a manual override always wins over an inactive schedule window. See the full priority order for exactly where scheduling sits relative to the rest of the chain.

ts
const { getSchedule, isScheduleActive } = useFeatureProvider()

getSchedule('holidayBanner') // → { from: '2025-12-01', to: '2025-12-31' }
isScheduleActive('holidayBanner') // → true only inside the window (or if no schedule is set)

getFlagSource('holidayBanner') returns 'schedule' while an inactive window is the reason the flag reads false.