Skip to content

Commit 9506271

Browse files
committed
feat: better docs for feature flags
1 parent ff63fb1 commit 9506271

File tree

1 file changed

+141
-8
lines changed

1 file changed

+141
-8
lines changed

apps/docs/content/docs/features/feature-flags.mdx

Lines changed: 141 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,56 @@ Feature flags allow you to control feature rollouts and conduct A/B testing with
1515

1616
## Quick Start
1717

18-
<Tabs items={['React/Next.js', 'Setup']}>
18+
<Tabs items={['React/Next.js with Auth', 'React/Next.js Basic', 'Setup']}>
19+
20+
<Tab>
21+
<CodeBlock language="tsx" filename="app.tsx">
22+
{`'use client';
23+
24+
import { FlagsProvider, useFlags } from '@databuddy/sdk/react';
25+
import { useSession } from '@databuddy/auth/client';
26+
27+
function App() {
28+
const { data: session, isPending } = useSession();
29+
30+
return (
31+
<FlagsProvider
32+
clientId="your-website-id"
33+
apiUrl="https://api.databuddy.cc"
34+
isPending={isPending}
35+
user={session?.user ? {
36+
userId: session.user.id,
37+
email: session.user.email,
38+
properties: {
39+
plan: session.user.plan || 'free',
40+
role: session.user.role || 'user',
41+
organization: session.user.organizationId,
42+
region: 'us-east', // or from user location
43+
}
44+
} : undefined}
45+
>
46+
<MyComponent />
47+
</FlagsProvider>
48+
);
49+
}
50+
51+
function MyComponent() {
52+
const { isEnabled, getValue } = useFlags();
53+
54+
const showNewFeature = isEnabled('new-dashboard');
55+
const showBetaFeatures = isEnabled('beta-features');
56+
const darkModeEnabled = getValue('dark-mode-default', false);
57+
58+
return (
59+
<div className={darkModeEnabled ? 'dark' : ''}>
60+
{showNewFeature && <NewDashboard />}
61+
{showBetaFeatures && <BetaFeatures />}
62+
<button>Click me</button>
63+
</div>
64+
);
65+
}`}
66+
</CodeBlock>
67+
</Tab>
1968

2069
<Tab>
2170
<CodeBlock language="tsx" filename="app.tsx">
@@ -40,9 +89,9 @@ function App() {
4089
4190
function MyComponent() {
4291
const { isEnabled } = useFlags();
43-
92+
4493
const showNewFeature = isEnabled('new-feature');
45-
94+
4695
return (
4796
<div>
4897
{showNewFeature && <NewFeature />}
@@ -87,9 +136,22 @@ const showNewUI = isEnabled('new-ui-rollout');`}
87136
apiUrl="https://api.databuddy.cc"
88137
user={{
89138
userId: currentUser.id,
90-
email: currentUser.email
139+
email: currentUser.email,
140+
properties: {
141+
plan: currentUser.plan || 'free',
142+
role: currentUser.role || 'user',
143+
organization: currentUser.organizationId,
144+
region: currentUser.region || 'us-east',
145+
signupDate: currentUser.createdAt,
146+
featureUsage: {
147+
reportsViewed: currentUser.reportsViewed || 0,
148+
dashboardsCreated: currentUser.dashboardsCreated || 0,
149+
}
150+
}
91151
}}
152+
isPending={isLoadingSession} // from useSession hook
92153
debug={process.env.NODE_ENV === 'development'}
154+
autoFetch={true} // default: true
93155
>
94156
<App />
95157
</FlagsProvider>`}
@@ -98,22 +160,60 @@ const showNewUI = isEnabled('new-ui-rollout');`}
98160
## Hook API
99161

100162
<CodeBlock language="tsx">
101-
{`const { isEnabled, fetchAllFlags, refresh } = useFlags();
163+
{`const {
164+
isEnabled,
165+
getValue,
166+
fetchAllFlags,
167+
updateUser,
168+
refresh
169+
} = useFlags();
102170
103171
// Check if flag is enabled
104172
const showFeature = isEnabled('my-feature');
173+
const darkModeDefault = getValue('dark-mode', false);
105174
106175
// Refresh all flags
107176
await fetchAllFlags();`}
108177
</CodeBlock>
109178

179+
## Why isPending Matters
180+
181+
The `isPending` prop is crucial for preventing race conditions and ensuring consistent user experiences:
182+
183+
- **Prevents Flash of Incorrect Content**: Without `isPending`, flags might evaluate with stale user data during authentication state changes
184+
- **Avoids Unnecessary API Calls**: The SDK waits for authentication to complete before making flag requests
185+
- **Consistent User Experience**: Users won't see feature toggles based on anonymous user data when they're actually logged in
186+
- **Better Performance**: Reduces redundant flag evaluations during session transitions
187+
188+
<CodeBlock language="tsx">
189+
{`// ❌ Bad: No isPending - flags evaluate with wrong user context
190+
<FlagsProvider user={undefined}>
191+
<App /> // Shows anonymous features briefly, then switches
192+
</FlagsProvider>
193+
194+
// ✅ Good: Waits for session before evaluating flags
195+
<FlagsProvider isPending={isPending} user={session?.user ? {...} : undefined}>
196+
<App /> // Shows correct features immediately
197+
</FlagsProvider>`}
198+
</CodeBlock>
199+
110200
## User Targeting
111201

112202
Target specific users or groups from your dashboard:
113203

114-
- **User ID**: Target specific users
115-
- **Email**: Target by email or domain
116-
- **Properties**: Target by custom user properties
204+
- **User ID**: Target specific users by their unique identifier
205+
- **Email**: Target by email address or domain (e.g., `@company.com`)
206+
- **Custom Properties**: Target by user attributes like plan tier, role, organization, region, signup date, or feature usage patterns
207+
208+
### Advanced Targeting with Custom Properties
209+
210+
Custom properties enable sophisticated targeting strategies:
211+
212+
- **Plan-based Rollouts**: `plan: 'premium'` → Show premium features only to paying users
213+
- **Geographic Targeting**: `region: 'us-east'` → Test features in specific regions
214+
- **Behavioral Targeting**: `featureUsage.reportsViewed > 10` → Target power users
215+
- **A/B Testing**: `experimentGroup: 'A'` → Segment users for experiments
216+
- **Time-based**: `signupDate > '2024-01-01'` → Target new vs. existing users
117217

118218
<CodeBlock language="tsx">
119219
{`<FlagsProvider
@@ -139,6 +239,39 @@ Target specific users or groups from your dashboard:
139239

140240
Enable debug mode to see flag evaluation in the browser console.
141241

242+
## Performance Benefits
243+
244+
- **Client-side Caching**: Flags are cached in IndexedDB and localStorage for instant loading
245+
- **Intelligent Updates**: Only re-evaluates flags when user context changes
246+
- **Background Sync**: Fetches flag updates without blocking the UI
247+
- **Minimal Bundle Size**: Lightweight SDK with zero external dependencies
248+
249+
## Best Practices
250+
251+
1. **Use `isPending`** with authentication to prevent race conditions
252+
2. **Pass Custom Properties** for granular targeting and A/B testing
253+
3. **Enable Debug Mode** during development to monitor flag evaluation
254+
4. **Handle Flag Changes** gracefully with loading states
255+
5. **Update User Context** after profile changes to refresh flag evaluation
256+
257+
<CodeBlock language="tsx">
258+
{`// Best practice: Handle loading states
259+
function FeatureComponent() {
260+
const { isEnabled } = useFlags();
261+
const [isLoading, setIsLoading] = useState(true);
262+
263+
useEffect(() => {
264+
// Simulate loading or wait for flags
265+
const timer = setTimeout(() => setIsLoading(false), 100);
266+
return () => clearTimeout(timer);
267+
}, []);
268+
269+
if (isLoading) return <Skeleton />;
270+
271+
return isEnabled('new-feature') ? <NewFeature /> : <OldFeature />;
272+
}`}
273+
</CodeBlock>
274+
142275
---
143276

144277
Ready to get started? [Create your first feature flag in the dashboard →](https://app.databuddy.cc/login)

0 commit comments

Comments
 (0)