diff --git a/guides/self-opt-in.md b/guides/self-opt-in.md index c1d3e57..57d419c 100644 --- a/guides/self-opt-in.md +++ b/guides/self-opt-in.md @@ -1,63 +1,147 @@ --- -description: How to create a beta feature self opt-in page in React with Reflag +description: How to build a beta feature opt-in page with the Reflag React SDK icon: browser --- # Beta feature opt-in -Creating a page where users can opt into certain beta/experimental features is straightforward with Reflag and the Reflag React SDK. +Let users opt themselves—or their company—into beta and experimental features with Reflag's React SDK. -The basic concept is to set an attribute on the user/company which denotes that the user has self-opted into a specific feature, for example `optin- = true.` Updating an attribute is very simple from the SDK. Then use that attribute to control who has access to the feature by updating the feature access rules such that users/companies with the attribute `optin-=true` will have access to the feature. +## Quick start -## Step-by-step guide +After enabling end-user opt-in on at least one flag in Reflag, render the available flags and let the current user set their opt-in status. -Here's a step-by-step guide: - -1. Select a feature to let people self-opt into. -2. Add the rule: `optin- IS TRUE` to the rules section for all the environments. Replace `` with the actual feature key of the feature. -3. Use the following React component to let users self-opt opt-in to specific features: +This example assumes your app has a `` boundary. See below for an example without ``. ```tsx -import { useUpdateUser, useFlag, ReflagFeatures } from "@reflag/react-sdk"; import { useState } from "react"; +import { + type OptInFlag, + useOptInFlags, + useSetOptIn, +} from "@reflag/react-sdk"; +import { Spinner } from "your-component-library"; + +function OptInPage() { + const { flags: optInFlags } = useOptInFlags({ suspense: true }); + + if (optInFlags.length === 0) { + return

No opt-in flags are available.

; + } + + return optInFlags.map((flag) => ( + + )); +} -function FeatureOptIn({ - featureKey, - featureName, -}: { - featureKey: ReflagFeatures; - featureName: string; -}) { - const updateUser = useUpdateUser(); - const [sendingUpdate, setSendingUpdate] = useState(false); - const { isEnabled } = useFlag(featureKey); +function OptInFlagCard({ flag }: { flag: OptInFlag }) { + const setOptIn = useSetOptIn(); + const [isUpdating, setIsUpdating] = useState(false); + const [updateError, setUpdateError] = useState(null); + const label = flag.userOptedIn ? "Cancel opt-in" : `Try ${flag.name}`; + + async function updateOptIn() { + setUpdateError(null); + setIsUpdating(true); + + try { + const response = await setOptIn(flag.key, { + optedIn: !flag.userOptedIn, + }); + + if (response?.ok === false) { + throw new Error("Opt-in request failed"); + } + } catch { + setUpdateError(`Could not update ${flag.name}. Please try again.`); + } finally { + setIsUpdating(false); + } + } return ( -
- - { - setSendingUpdate(true); - updateUser({ - [`optin-${featureKey}`]: isEnabled ? "false" : "true", - }).then(() => { - setSendingUpdate(false); - }); - }} - /> -
+
+

{flag.name}

+ {flag.description &&

{flag.description}

} + + {updateError &&

{updateError}

} +
); } ``` -### How it works +`setOptIn()` returns a promise that resolves after the SDK applies the latest flag state, confirms the membership change, and notifies components using `useOptInFlags()`. React may not have committed the resulting render yet. + +`useOptInFlags()` keeps the list synchronized with Reflag. `useSetOptIn()` changes the current user's opt-in by default and requires the current Reflag context to include a `user.id`. + +## Configure a flag for opt-in + +1. Open a non-secret flag in Reflag. +2. Go to **Settings > Opt-in**. +3. Enable **End-user opt-in**. +4. Optionally add a **Public description**. The SDK exposes this text so you can display it in your opt-in UI. +5. Save your changes. +6. On the flag's **Access** tab, verify that access is set to **Some** in each environment where users should be able to opt in. Leave the other access rules empty for an opt-in-only feature, or add rules to grant access through either targeting or opt-in. + +Secret flags cannot use end-user opt-in because opt-ins are submitted directly from a browser or client using a publishable key. + +## Company opt-in + +To change the current company's opt-in, pass `scope: "company"`. The current Reflag context must include a `company.id`. + +```tsx +setOptIn(flag.key, { + optedIn: !flag.companyOptedIn, + scope: "company", +}); +``` + +User and company opt-ins are independent. Setting `optedIn` to `false` removes only the selected scope, so `isOptedIn` remains `true` while either scope is opted in. + +Cancelling every opt-in does not necessarily disable the flag: an access rule may independently enable it for the current context. + +## Access behavior + +A flag's access setting determines how opt-in membership affects evaluation: + +| Access | Behavior | +| --- | --- | +| **No one** | The flag is off for everyone. Existing opt-ins are inactive, and new opt-ins are rejected. | +| **Some** | The flag is enabled when another access rule matches **or** the current user or company opted in. With no other rules, access is opt-in-only. | +| **Everyone** | The flag is enabled for everyone, regardless of opt-in status. | + +Disabling end-user opt-in stops new opt-ins and makes existing memberships inactive, but it does not delete them. Re-enabling opt-in reactivates those memberships unless access is set to **No one**. + +## Managing loading state with `` and without `` + +Only apps using `ReflagBootstrappedProvider` without Suspense need to handle this loading state. Bootstrapped flag data does not include opt-in metadata, so the SDK fetches it when `useOptInFlags()` is first used. + +Check the hook's `isLoading` value before rendering an empty state: + +```tsx +const { flags: optInFlags, isLoading } = useOptInFlags({ suspense: false }); + +if (isLoading) { + return ; +} + +if (optInFlags.length === 0) { + return

No opt-in flags are available.

; +} +``` -The React component above uses [remote attributes](https://reflag.com/changelog/introducing-remote-attributes) to ensure that any feature you've enabled stays enabled between sessions. +With a regular `ReflagProvider`, opt-in metadata arrives as part of the normal flags request, so `useOptInFlags().isLoading` remains `false`. Use `useIsLoading()` or the provider's `loadingComponent` for the normal initial loading state. -### Next steps +## Next steps -* Learn how to manage who has access, and modify the [Targeting rules](../product-handbook/feature-rollouts/feature-targeting-rules.md) in the UI. +Learn how to manage additional access with [Access rules](../product-handbook/feature-rollouts/feature-targeting-rules.md). diff --git a/product-handbook/self-opt-in.md b/product-handbook/self-opt-in.md deleted file mode 100644 index 9c6afe0..0000000 --- a/product-handbook/self-opt-in.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -description: How to create a beta feature self opt-in page in React with Reflag -icon: browser ---- - -# Create a beta feature opt-in page - -Creating a page where users can opt into certain beta/experimental features is straight forward with Reflag and the Reflag React SDK. - -The basic concept is to set an attribute on the user/company which denotes that the user has self opted into a specific feature, for example `optin- = true.` Updating an attribute is very simple from the SDK. Then use that attribute to control who has access to the feature by updating the flag access rules such that users/companies with the attribute `optin-=true` will have access to the feature. - -## Step by step guide - -Here's a step by step guide: - -1. Select a feature to let people self opt-into. -2. Add the rule: `optin- IS TRUE` to the rules section for all the environments. Replace `` with the actual flag key of the flag. -3. Use the following React component to let users self opt-in to specific features: - -```tsx -import { useUpdateUser, useFlag, TypedFlags } from "@reflag/react-sdk"; -import { useState } from "react"; - -function FeatureOptIn({ - flagKey, - featureName, -}: { - flagKey: TypedFlags; - featureName: string; -}) { - const updateUser = useUpdateUser(); - const [sendingUpdate, setSendingUpdate] = useState(false); - const { isEnabled } = useFlag(flagKey); - - return ( -
- - { - setSendingUpdate(true); - updateUser({ - [`optin-${flagKey}`]: isEnabled ? "false" : "true", - }).then(() => { - setSendingUpdate(false); - }); - }} - /> -
- ); -} -``` - -### How it works - -The React component above uses [remote attributes](https://reflag.com/changelog/introducing-remote-attributes) to ensure that any flag you've enabled stays enabled between sessions. - -### Next steps - -- Learn how to manage who has access, modify the [Targeting rules](feature-rollouts/feature-targeting-rules.md) in the UI.