Feature flags your build can decide
The build decides every flag it can before deploy, and only flags that depend on the visitor wait. Decouple release from deployment.
Your flags live in your repo as a file, changed in the same pull request as your code.
npm install @evanion/featureDecided before deploy
Set once for every visitor when the site builds.
Brass: Birmingham
Brass: Birmingham is a strategy game, so the rollout can reach its visitors.
New gallery for some visitors
The new gallery waits for the visitor.
Decided when a visitor arrives
Set for each visitor when they arrive.
11 of 48 visitors see the new gallery.
Raising the share only adds visitors; nobody who has the new gallery loses it.
Each dot is a visitor, told apart by their id. Filled means they see the new gallery. The same visitor always gets the same answer.
Tap or hover a dot to see that visitor.
Your flags
createFeatures([
{ key: 'new-gallery', enabled: true,
rules: [{
when: [{ field: 'category', op: 'eq', value: 'strategy' }],
rollout: { percent: 25, by: 'targetingKey' },
}] },
])
// before deploy: one call per page
features.plan({ game, category: 'strategy' })
// when a visitor arrives: one call per request
features.resolve({ game, category, targetingKey: visitor.id })
A file in your own repo
Your flags are a file in your app's own repo. You change them in the same pull request as the code that uses them and review them like code.
No network call per check
Your API, your build and the browser answer flags by themselves.
Update flags without a deploy
Serve the file as a plain asset and reload it in the running app.
How reloading works →No dashboard included
This is a library. You decide how your team edits the flag file.
Stop the domino effect
Your customers never see a feature whose foundation is gone.
New features often rely on older ones. Turning off a foundation feature can leave dependent features in a broken state.
You can make features depend on one another. Before you commit, the toggle lists exactly what will go off. After the change, each feature says why it is off.
- New checkouton
- Express pickupon
- Demo-night bookingon
- Express pickupon
createFeatures([
{ key: 'new-checkout', enabled: true },
{ key: 'express-pickup', enabled: true, dependsOn: ['new-checkout'] },
{ key: 'demo-night-booking', enabled: true, dependsOn: ['express-pickup'] },
])
Tests that don't jump
Run clean A/B tests where visitors stay in their assigned group.
Shuffling people between versions mid-test ruins your data and confuses your users.
Visitors who keep their ID keep their variant when you move a two-way split. Only those between the old and new boundary change. You can also pin a specific group to one variant.
25 of 48 see the blue button · Move the split to see who changes
Each dot is a visitor. Filled ones see the blue button, hollow ones the original. A dashed outline is a bookseller pinned to blue. A ring is a visitor who switched just now.
Tap or hover a dot to see that visitor.
{ key: 'cta-color', enabled: true,
variants: [{ name: 'control', weight: 50 }, { name: 'blue', weight: 50 }],
rules: [],
}
Launch while you sleep
Set your release date and forget it.
You want to coordinate a big launch without being awake to flip a switch or trigger a deployment.
A rule turns a feature on after the time you choose. Because the decision depends only on the clock, the server and browser always stay in sync.
See how it works →No flicker on load
Eliminate the jarring visual jump on page load.
Pages that decide flags only in the browser often show a default version for a split second before swapping to the correct one.
The server decides the flags and sends those decisions inside the page. The browser uses them before the first paint and can check the server's decisions against its own copy to report differences.
See how it works →Built for React
Wrap your app once and any component can ask if a feature is on.
npm install @evanion/feature @evanion/react-featureimport { createFeatures } from '@evanion/feature';
import { FeatureProvider, useFeatureEnabled } from '@evanion/react-feature';
const features = createFeatures([{ key: 'new-checkout', enabled: true }]);
function Checkout() {
const isNew = useFeatureEnabled('new-checkout');
return isNew ? <p>New checkout</p> : <p>Old checkout</p>;
}
function App() {
return (
<FeatureProvider features={features}>
<Checkout />
</FeatureProvider>
);
}Read the guide →Questions
- What is @evanion/feature?
- It is an open source TypeScript library (MIT). You define your rules, rollouts, variants, and dependencies in a file and call it from your API, build, or browser.
- Does it need a server of its own?
- No. There is no service to call. The file ships with your app, or you can serve it as a plain file to reload it in a running app.
- How do anonymous visitors get a stable variant?
- Bring your own visitor ID. The library never sets a cookie and never stores which variant anyone got. Signed-in visitors use the account ID. Anonymous visitors use an ID the site already sets, such as a session, cart or analytics ID. If the site has none, you can set a first-party ID cookie yourself, under your own consent rules. Without any ID, the visitor gets the default variant, and percentage rollouts leave them out. To keep a variant across sign-in, use
variantByorstickyVariants. - Does it work with static sites?
- Yes. A build decides every flag it has enough information for and writes the answer into the page. Flags that depend on the visitor are decided when the visitor arrives.
- Is there React support?
- Yes, via @evanion/react-feature. It provides a provider and hooks to access flags anywhere. Server decisions reach the browser so there is no flicker.
Feature flags your build can decide
The build decides what it can before deploy, and only flags that depend on the visitor wait. Decouple release from deployment. Install the library and follow the getting started guide to begin.