diff --git a/apps/docs/content/docs/features/feature-flags.mdx b/apps/docs/content/docs/features/feature-flags.mdx index 0a374e931..9ebfe33ac 100644 --- a/apps/docs/content/docs/features/feature-flags.mdx +++ b/apps/docs/content/docs/features/feature-flags.mdx @@ -19,84 +19,84 @@ Feature flags allow you to control feature rollouts and conduct A/B testing with -{`'use client'; - -import { FlagsProvider, useFeature } from '@databuddy/sdk/react'; -import { useSession } from '@databuddy/auth/client'; - -function App() { - const { data: session, isPending } = useSession(); - - return ( - - - - ); -} + {`'use client'; + + import { FlagsProvider, useFeature } from '@databuddy/sdk/react'; + import { useSession } from '@databuddy/auth/client'; + + function App() { + const { data: session, isPending } = useSession(); + + return ( + + + + ); + } -function MyComponent() { - const { on: isDarkMode, loading } = useFeature('dark-mode'); - const { on: showNewDashboard } = useFeature('new-dashboard'); + function MyComponent() { + const { on: isDarkMode, loading } = useFeature('dark-mode'); + const { on: showNewDashboard } = useFeature('new-dashboard'); - if (loading) return ; + if (loading) return ; - return ( -
- {showNewDashboard ? : } -
- ); -}`} + return ( +
+ {showNewDashboard ? : } +
+ ); + }`}
-{`'use client'; - -import { FlagsProvider, useFeature } from '@databuddy/sdk/react'; - -function App() { - return ( - - - - ); -} - -function MyComponent() { - const { on, loading } = useFeature('new-feature'); + {`'use client'; + + import { FlagsProvider, useFeature } from '@databuddy/sdk/react'; + + function App() { + return ( + + + + ); + } - if (loading) return ; - return on ? : ; -}`} + function MyComponent() { + const { on, loading } = useFeature('new-feature'); + + if (loading) return ; + return on ? : ; + }`} -{`# Install the SDK -bun add @databuddy/sdk`} + {`# Install the SDK + bun add @databuddy/sdk`} @@ -109,14 +109,14 @@ bun add @databuddy/sdk`} The simplest way to check if a feature is enabled. Returns an object with `{ on, loading, status, value, variant }`. -{`import { useFeature } from '@databuddy/sdk/react'; - -function MyComponent() { - const { on, loading } = useFeature('new-dashboard'); - - if (loading) return ; - return on ? : ; -}`} + {`import { useFeature } from '@databuddy/sdk/react'; + + function MyComponent() { + const { on, loading } = useFeature('new-dashboard'); + + if (loading) return ; + return on ? : ; + }`} ### `useFeatureOn(key, defaultValue)` - SSR-Safe Boolean @@ -124,18 +124,18 @@ function MyComponent() { Returns a boolean directly with a default value. Perfect for SSR where you need an immediate value. -{`import { useFeatureOn } from '@databuddy/sdk/react'; - -function MyComponent() { - // Returns false while loading, then the actual value - const isDarkMode = useFeatureOn('dark-mode', false); - - return ( -
- Content -
- ); -}`} + {`import { useFeatureOn } from '@databuddy/sdk/react'; + + function MyComponent() { + // Returns false while loading, then the actual value + const isDarkMode = useFeatureOn('dark-mode', false); + + return ( +
+ Content +
+ ); + }`}
### `useFlag(key)` - Full State Control @@ -143,22 +143,22 @@ function MyComponent() { Get the complete flag state with all properties including status and error handling. -{`import { useFlag } from '@databuddy/sdk/react'; - -function MyComponent() { - const flag = useFlag('experiment'); - - switch (flag.status) { - case 'loading': - return ; - case 'error': - return ; - case 'ready': - return flag.on ? : ; - case 'pending': - return ; - } -}`} + {`import { useFlag } from '@databuddy/sdk/react'; + + function MyComponent() { + const flag = useFlag('experiment'); + + switch (flag.status) { + case 'loading': + return ; + case 'error': + return ; + case 'ready': + return flag.on ? : ; + case 'pending': + return ; + } + }`} ### `useFlagValue(key, defaultValue)` - Typed Values @@ -166,18 +166,18 @@ function MyComponent() { Get typed values for string or number flags. -{`import { useFlagValue } from '@databuddy/sdk/react'; - -function MyComponent() { - const maxItems = useFlagValue('max-items', 10); - const theme = useFlagValue<'light' | 'dark'>('theme', 'light'); - - return ( -
- Showing {maxItems} items -
- ); -}`} + {`import { useFlagValue } from '@databuddy/sdk/react'; + + function MyComponent() { + const maxItems = useFlagValue('max-items', 10); + const theme = useFlagValue<'light' | 'dark'>('theme', 'light'); + + return ( +
+ Showing {maxItems} items +
+ ); + }`}
### `useVariant(key)` - A/B Testing @@ -185,16 +185,16 @@ function MyComponent() { Get the variant name for multivariate testing. -{`import { useVariant } from '@databuddy/sdk/react'; - -function CheckoutPage() { - const variant = useVariant('checkout-experiment'); - - if (variant === 'control') return ; - if (variant === 'variant-a') return ; - if (variant === 'variant-b') return ; - return ; -}`} + {`import { useVariant } from '@databuddy/sdk/react'; + + function CheckoutPage() { + const variant = useVariant('checkout-experiment'); + + if (variant === 'control') return ; + if (variant === 'variant-a') return ; + if (variant === 'variant-b') return ; + return ; + }`} ## Flag Types @@ -203,23 +203,23 @@ function CheckoutPage() { Simple on/off switches for features. -{`const { on } = useFeature('my-feature');`} + {`const { on } = useFeature('my-feature');`} ### String/Number Flags For configuration values and multivariate testing. -{`const maxUsers = useFlagValue('max-users', 100); -const theme = useFlagValue<'light' | 'dark'>('theme', 'light');`} + {`const maxUsers = useFlagValue('max-users', 100); + const theme = useFlagValue<'light' | 'dark'>('theme', 'light');`} ### Rollout Flags Gradually roll out features to a percentage of users. -{`// Set rollout percentage in dashboard (e.g., 25%) -const { on } = useFeature('new-ui-rollout');`} + {`// Set rollout percentage in dashboard (e.g., 25%) + const { on } = useFeature('new-ui-rollout');`} ### Multivariant Flags (A/B/n Testing) @@ -227,26 +227,26 @@ const { on } = useFeature('new-ui-rollout');`} Run experiments with multiple variants. Each user is consistently assigned to a variant based on their identifier. -{`import { useVariant, useFeature } from '@databuddy/sdk/react'; - -function CheckoutPage() { - const variant = useVariant('checkout-experiment'); - const { value, loading } = useFeature('checkout-experiment'); - - if (loading) return ; - - // Variant-based rendering - switch (variant) { - case 'control': - return ; - case 'simplified': - return ; - case 'one-click': - return ; - default: - return ; - } -}`} + {`import { useVariant, useFeature } from '@databuddy/sdk/react'; + + function CheckoutPage() { + const variant = useVariant('checkout-experiment'); + const { value, loading } = useFeature('checkout-experiment'); + + if (loading) return ; + + // Variant-based rendering + switch (variant) { + case 'control': + return ; + case 'simplified': + return ; + case 'one-click': + return ; + default: + return ; + } + }`} **Creating multivariant flags in the dashboard:** @@ -269,32 +269,32 @@ function CheckoutPage() { ## Configuration -{` - -`} + }} + isPending={isLoadingSession} + debug={process.env.NODE_ENV === 'development'} + autoFetch={true} + cacheTtl={60000} // Cache for 1 minute + staleTime={30000} // Revalidate after 30s + > + + `} ### Configuration Options @@ -318,43 +318,43 @@ function CheckoutPage() { Access the flags context directly for more control: -{`import { useFlags } from '@databuddy/sdk/react'; - -function MyComponent() { - const { - isOn, // Simple boolean check - getFlag, // Get full flag state - getValue, // Get typed value - fetchFlag, // Async fetch single flag - fetchAllFlags, // Async fetch all flags - updateUser, // Update user context - refresh, // Refresh all flags - isReady // SDK ready state - } = useFlags(); - - // Simple check - if (isOn('premium-feature')) { - // Show premium content - } + {`import { useFlags } from '@databuddy/sdk/react'; + + function MyComponent() { + const { + isOn, // Simple boolean check + getFlag, // Get full flag state + getValue, // Get typed value + fetchFlag, // Async fetch single flag + fetchAllFlags, // Async fetch all flags + updateUser, // Update user context + refresh, // Refresh all flags + isReady // SDK ready state + } = useFlags(); + + // Simple check + if (isOn('premium-feature')) { + // Show premium content + } - // Get full state - const flag = getFlag('experiment'); - console.log(flag.on, flag.loading, flag.status); - - // Refresh flags - const handleRefresh = async () => { - await refresh(); - }; - - // Update user context - const handleUpgrade = async () => { - updateUser({ - userId: user.id, - email: user.email, - properties: { plan: 'premium' } - }); - }; -}`} + // Get full state + const flag = getFlag('experiment'); + console.log(flag.on, flag.loading, flag.status); + + // Refresh flags + const handleRefresh = async () => { + await refresh(); + }; + + // Update user context + const handleUpgrade = async () => { + updateUser({ + userId: user.id, + email: user.email, + properties: { plan: 'premium' } + }); + }; + }`} ## Flag States @@ -362,18 +362,18 @@ function MyComponent() { The `FlagState` object has the following properties: -{`interface FlagState { - on: boolean; // Whether the flag is enabled - loading: boolean; // Whether the flag is loading - status: FlagStatus; // 'loading' | 'ready' | 'error' | 'pending' - value?: boolean | string | number; // The flag's value - variant?: string; // Variant for A/B tests - - // Deprecated (use above instead): - enabled: boolean; // Use 'on' instead - isLoading: boolean; // Use 'loading' instead - isReady: boolean; // Use 'status === "ready"' instead -}`} + {`interface FlagState { + on: boolean; // Whether the flag is enabled + loading: boolean; // Whether the flag is loading + status: FlagStatus; // 'loading' | 'ready' | 'error' | 'pending' + value?: boolean | string | number; // The flag's value + variant?: string; // Variant for A/B tests + + // Deprecated (use above instead): + enabled: boolean; // Use 'on' instead + isLoading: boolean; // Use 'loading' instead + isReady: boolean; // Use 'status === "ready"' instead + }`} ## Performance Features @@ -382,18 +382,18 @@ The `FlagState` object has the following properties: Flags return cached values immediately while revalidating in the background. -{`// Returns cached value instantly, revalidates if stale -const { on } = useFeature('my-feature'); // Instant!`} + {`// Returns cached value instantly, revalidates if stale + const { on } = useFeature('my-feature'); // Instant!`} ### Request Batching Multiple flag requests within 10ms are batched into a single API call. -{`// These 3 calls become 1 API request -const feature1 = useFeature('feature-1'); -const feature2 = useFeature('feature-2'); -const feature3 = useFeature('feature-3');`} + {`// These 3 calls become 1 API request + const feature1 = useFeature('feature-1'); + const feature2 = useFeature('feature-2'); + const feature3 = useFeature('feature-3');`} ### Visibility API @@ -407,18 +407,18 @@ Identical requests are deduplicated automatically. The `isPending` prop prevents race conditions during authentication: -{`// ❌ Bad: Flags evaluate with wrong user context - - // Shows anonymous features, then switches - - -// ✅ Good: Waits for session before evaluating - - // Shows correct features immediately -`} + {`// ❌ Bad: Flags evaluate with wrong user context + + // Shows anonymous features, then switches + + + // ✅ Good: Waits for session before evaluating + + // Shows correct features immediately + `} **Benefits:** @@ -438,21 +438,21 @@ Target specific users or groups from your dashboard: ### Advanced Targeting with Custom Properties -{``} + }} + >`} **Targeting Examples:** @@ -466,87 +466,87 @@ Target specific users or groups from your dashboard: ### ✅ Use the Right Hook -{`// Simple boolean check -const { on, loading } = useFeature('dark-mode'); + {`// Simple boolean check + const { on, loading } = useFeature('dark-mode'); -// SSR-safe with default -const isDarkMode = useFeatureOn('dark-mode', false); + // SSR-safe with default + const isDarkMode = useFeatureOn('dark-mode', false); -// Full control with status -const flag = useFlag('experiment'); -if (flag.status === 'ready') { ... } + // Full control with status + const flag = useFlag('experiment'); + if (flag.status === 'ready') { ... } -// Typed values -const maxItems = useFlagValue('max-items', 10); + // Typed values + const maxItems = useFlagValue('max-items', 10); -// A/B test variants -const variant = useVariant('checkout-test');`} + // A/B test variants + const variant = useVariant('checkout-test');`} ### ✅ Handle Loading States -{`function FeatureComponent() { - const { on, loading } = useFeature('new-feature'); - - if (loading) return ; - return on ? : ; -}`} + {`function FeatureComponent() { + const { on, loading } = useFeature('new-feature'); + + if (loading) return ; + return on ? : ; + }`} ### ✅ Multiple Flags -{`function Dashboard() { - const { on: darkMode } = useFeature('dark-mode'); - const { on: newLayout } = useFeature('new-layout'); - const maxItems = useFlagValue('max-items', 10); - - return ( -
- {newLayout ? : } -
- ); -}`} + {`function Dashboard() { + const { on: darkMode } = useFeature('dark-mode'); + const { on: newLayout } = useFeature('new-layout'); + const maxItems = useFlagValue('max-items', 10); + + return ( +
+ {newLayout ? : } +
+ ); + }`}
### ✅ Update User Context -{`function UpgradeButton() { - const { updateUser } = useFlags(); - - const handleUpgrade = () => { - // Update user context to refresh flags - updateUser({ - userId: user.id, - email: user.email, - properties: { plan: 'premium' } - }); - }; - - return ; -}`} + {`function UpgradeButton() { + const { updateUser } = useFlags(); + + const handleUpgrade = () => { + // Update user context to refresh flags + updateUser({ + userId: user.id, + email: user.email, + properties: { plan: 'premium' } + }); + }; + + return ; + }`} ## Migration from Old API -{`// ❌ Old (confusing) -const { isEnabled } = useFlags(); -const flag = isEnabled('my-feature'); -if (flag.isReady && flag.enabled) { - return ; -} - -// ✅ New (clear) -const { on, loading } = useFeature('my-feature'); -if (loading) return ; -return on ? : ; - -// Or even simpler for SSR -const isOn = useFeatureOn('my-feature', false); -return isOn ? : ;`} + {`// ❌ Old (confusing) + const { isEnabled } = useFlags(); + const flag = isEnabled('my-feature'); + if (flag.isReady && flag.enabled) { + return ; + } + + // ✅ New (clear) + const { on, loading } = useFeature('my-feature'); + if (loading) return ; + return on ? : ; + + // Or even simpler for SSR + const isOn = useFeatureOn('my-feature', false); + return isOn ? : ;`} The old API (`isEnabled`) is still supported for backwards compatibility but deprecated. @@ -556,10 +556,10 @@ The old API (`isEnabled`) is still supported for backwards compatibility but dep Enable debug mode to see flag evaluation in the console: -{``} + {``} ---