Features
Feature Flags
Ship code switched off, then turn it on from the dashboard: for everyone, a percentage, or the users you pick. No redeploy.
Get started
Three steps. You need the Flowgrid SDK installed. If you haven't, start with the SDK Quickstart.
1. Create a flag
In the dashboard, open Feature flags → Create new flag and give it a key, like new-checkout. It starts switched off.
2. Wrap your code
Read the flag where the feature renders. While it's off, missing, or Flowgrid can't be reached, you get false, so your current code keeps running. Reading a flag never throws.
1import { useFlag } from 'flowgrid-sdk/react';
2
3function CheckoutPage() {
4 const enabled = useFlag('new-checkout');
5 return enabled ? <NewCheckout /> : <Checkout />;
6}Every flag in the dashboard shows this code for its own key, with a Ship it with Coder button that adds it to your codebase as a pull request.
3. Roll it out
Switch the flag on. With no rules it's on for everyone. To go slower, add a percentage rule and raise it as you gain confidence. Raising it only adds users; nobody who had the feature loses it. Changes reach users on their next page load.
Prefer to create flags from code?
List the key in declare and it appears in the dashboard, switched off. Declaring never turns a flag on or changes one that already exists.
1FlowGrid.init({
2 webId: 'YOUR_WEB_ID',
3 flags: { declare: ['new-checkout'] },
4});Payloads
A flag can carry a value as well as on/off: a headline, a limit, a whole config object. Add it under Attach a payload on the flag and read it with get(). While the flag is off, you get the default you pass.
1import { useFlagValue } from 'flowgrid-sdk/react';
2
3// Payload in the dashboard: { "heading": "Try it free", "trialDays": 14 }
4const { heading, trialDays } = useFlagValue('pricing-hero', {
5 heading: 'Get started',
6 trialDays: 7,
7});Payloads are any JSON up to 32KB. The dashboard won't save JSON that doesn't parse, so your code never receives a broken object as a string.
A/B variants
Change the flag's kind to Variants (A/B test) and name its versions, for example control and urgent. Each user always gets the same one.
1import { useFlagVariant } from 'flowgrid-sdk/react';
2
3const variant = useFlagVariant('checkout-copy');
4return variant === 'urgent' ? <UrgentCheckout /> : <Checkout />;Each variant can carry its own payload, read with get() as above. For a value you change without testing, like a retry limit, choose Settings value instead.
Target users
Add rules to choose who gets the flag. They're checked top to bottom and the first match decides; everyone else gets the flag's default. Switching the flag off overrides every rule.
Click + and on a rule to require more than one thing at once, like plan is pro and 25% of users for a slow rollout to paying customers, or plan is pro and in Germany. Every condition on a rule must hold; up to five.
| Rule | Example | What your code adds |
|---|---|---|
| A percentage of users | 10%, then 50%, then 100% | Nothing |
| Users with a property | plan is pro, or role is admin | setUser(id, { plan }) |
| Users who did something | signed up, or filter_applied in the last 7 days | capture('filter_applied') for your own events |
| Users in a country | US, GB | Nothing |
| An environment | production only | Nothing in the browser |
Properties come from the traits your app sends with the signed-in user, on every page load. Add them to the trackAuth call at sign-in too. See Identify users.
1fg.setUser(user.id, { email: user.email, plan: user.plan, role: user.role });Property, event and country rules are decided on Flowgrid's servers, so the list of who's included never reaches a user's browser. You read those flags the same way.
Custom events
Users who did somethingcan target Flowgrid's own user actions (signed up, logged in, finished onboarding, submitted a form) or your own events. Send your own with capture(): any interaction in your app, as often as it happens.
1fg.capture('filter_applied', { filter: 'price', value: 'under-50' });Events appear in the rule's event picker once they arrive. To target one before your code sends it, type its name in the picker and choose Create event.capture() never throws. Names Flowgrid uses for its own events, like page_load, are refused with a console warning.
Server-side
getServerFlags() reads every flag with one request and answers on(), variant() and get(). A browser fills in the user's details itself; a server can't, so pass them.
1import { cookies } from 'next/headers';
2import { getServerFlags, visitorIdFromCookies } from 'flowgrid-sdk/server';
3
4const flags = await getServerFlags({
5 webId: process.env.NEXT_PUBLIC_FLOWGRID_WEB_ID!,
6 context: {
7 visitorId: visitorIdFromCookies(await cookies()),
8 userId: session?.user.id,
9 attributes: { plan: session?.user.plan ?? 'free' },
10 },
11 // Flags with property, event or country rules
12 keys: ['enterprise-pricing'],
13});Always pass visitorId
It keeps a signed-out user in the same group on the server as in their browser. Without it, a 10% rollout is on for everyone or for no one.
Measure impact
Turn on Measure this flag to compare the users who got it with those who didn't: errors from the feature, page speed, and the conversion event you pick. Use a percentage rule when you measure. A property or country rule hands the feature to a different kind of user, so the comparison isn't fair.
To count only errors from the new code, run it through the flag. If it throws, the user gets the old path instead of a broken page:
1const total = fg.flags.run('new-pricing', () => priceV2(cart), () => price(cart));Debugging
Ask why a flag gave the answer it did:
1flags.explain('new-checkout');
2// { value: false, reason: 'default', source: 'persistence' }
3// reason: kill_switch | rule | default | unknown_flagFlags are cached in the browser so they're instant on return visits, and refresh on each page load. Change caching under Feature flags → Delivery. Turning tracking off for a page doesn't switch flags off there; block Feature flags for that page explicitly.
Clean up
A flag is marked Ready to clean up once it's been unchanged for 30 days and is off, on for everyone, or no longer read by your code. Remove it from your code, then archive it. An archived flag returns the default your code passes.