SDK Reference

Feature Flags (Client)

Feature flags allow you to control feature rollouts and conduct A/B testing without deploying new code. Enable or disable features instantly from your dashboard.

Quick Start

app.tsxtsx
'use client';

import { FlagsProvider, useFeature } from '@databuddy/sdk/react';
import { useSession } from '@databuddy/auth/client';

function App() {
  const { data: session, isPending } = useSession();

  return (
    <FlagsProvider
      clientId="your-website-id"
      apiUrl="https://api.databuddy.cc"
      isPending={isPending}
      user={session?.user ? {
        userId: session.user.id,
        email: session.user.email,
        organizationId: session.user.organizationId,
        teamId: session.user.teamId,
        properties: {
          plan: session.user.plan || 'free',
          role: session.user.role || 'user',
          region: 'us-east',
        }
      } : undefined}
    >
      <MyComponent />
    </FlagsProvider>
  );
}

function MyComponent() {
  const { on: isDarkMode, loading } = useFeature('dark-mode');
  const { on: showNewDashboard } = useFeature('new-dashboard');

  if (loading) return <Skeleton />;

  return (
    <div className={isDarkMode ? 'dark' : ''}>
      {showNewDashboard ? <NewDashboard /> : <OldDashboard />}
    </div>
  );
}

Hooks API

useFeature(key) - Simple Feature Check

The simplest way to check if a feature is enabled. Returns an object with { on, loading, status, value, variant }.

tsx
import { useFeature } from '@databuddy/sdk/react';

function MyComponent() {
  const { on, loading } = useFeature('new-dashboard');
  
  if (loading) return <Skeleton />;
  return on ? <NewDashboard /> : <OldDashboard />;
}

useFeatureOn(key, defaultValue) - SSR-Safe Boolean

Returns a boolean directly with a default value. Perfect for SSR where you need an immediate value.

tsx
import { useFeatureOn } from '@databuddy/sdk/react';

function MyComponent() {
  // Returns false while loading, then the actual value
  const isDarkMode = useFeatureOn('dark-mode', false);
  
  return (
    <div className={isDarkMode ? 'dark' : 'light'}>
      Content
    </div>
  );
}

useFlag(key) - Full State Control

Get the complete flag state with all properties including status and error handling.

tsx
import { useFlag } from '@databuddy/sdk/react';

function MyComponent() {
  const flag = useFlag('experiment');
  
  switch (flag.status) {
    case 'loading':
      return <Skeleton />;
    case 'error':
      return <ErrorFallback />;
    case 'ready':
      return flag.on ? <NewFeature /> : <OldFeature />;
    case 'pending':
      return <LoadingState />;
  }
}

useFlagValue(key, defaultValue) - Typed Values

Get typed values for string or number flags.

tsx
import { useFlagValue } from '@databuddy/sdk/react';

function MyComponent() {
  const maxItems = useFlagValue('max-items', 10);
  const theme = useFlagValue<'light' | 'dark'>('theme', 'light');
  
  return (
    <div className={theme}>
      Showing {maxItems} items
    </div>
  );
}

useVariant(key) - A/B Testing

Get the variant name for multivariate testing.

tsx
import { useVariant } from '@databuddy/sdk/react';

function CheckoutPage() {
  const variant = useVariant('checkout-experiment');
  
  if (variant === 'control') return <OldCheckout />;
  if (variant === 'variant-a') return <NewCheckoutA />;
  if (variant === 'variant-b') return <NewCheckoutB />;
  return <DefaultCheckout />;
}

Flag Types

Boolean Flags

Simple on/off switches for features.

tsx
const { on } = useFeature('my-feature');

String/Number Flags

For configuration values and multivariate testing.

tsx
const maxUsers = useFlagValue('max-users', 100);
const theme = useFlagValue<'light' | 'dark'>('theme', 'light');

Rollout Flags

Gradually roll out features to a percentage of users.

tsx
// Set rollout percentage in dashboard (e.g., 25%)
const { on } = useFeature('new-ui-rollout');

Rollout Units

Control how users are grouped for rollouts. In the dashboard, choose the Rollout Unit:

UnitDescriptionUse Case
UserEach user individuallyStandard per-user rollouts
OrganizationAll org members togetherEnterprise features, billing changes
TeamAll team members togetherTeam-specific features
org-rollout.tsxtsx
// Pass organizationId for org-level rollouts
<FlagsProvider
  clientId="your-website-id"
  user={{
    userId: "user_123",
    email: "user@company.com",
    organizationId: "org_abc",  // All org members get same result
    teamId: "team_456",         // All team members get same result
  }}
>
  <App />
</FlagsProvider>

Multivariant Flags (A/B/n Testing)

Run experiments with multiple variants. Each user is consistently assigned to a variant based on their identifier.

checkout-experiment.tsxtsx
import { useVariant, useFeature } from '@databuddy/sdk/react';

function CheckoutPage() {
  const variant = useVariant('checkout-experiment');
  const { value, loading } = useFeature('checkout-experiment');
  
  if (loading) return <Skeleton />;
  
  // Variant-based rendering
  switch (variant) {
    case 'control':
      return <CurrentCheckout />;
    case 'simplified':
      return <SimplifiedCheckout />;
    case 'one-click':
      return <OneClickCheckout />;
    default:
      return <CurrentCheckout />;
  }
}

Creating multivariant flags in the dashboard:

  1. Select "multivariant" as the flag type
  2. Add your variants with keys and values (string, number, or JSON)
  3. Set traffic weights for each variant (must sum to 100%)
  4. Optionally add targeting rules to show specific variants to certain users

Use cases:

  • A/B Testing: Compare two versions of a feature
  • Pricing experiments: Test different price points
  • UI variations: Test layouts, colors, copy
  • Multi-armed bandits: Run experiments with 3+ variants

Configuration

provider.tsxtsx
<FlagsProvider
  clientId="your-website-id"
  apiUrl="https://api.databuddy.cc"
  user={{
    userId: currentUser.id,
    email: currentUser.email,
    organizationId: currentUser.organizationId,  // For org-level rollouts
    teamId: currentUser.teamId,                  // For team-level rollouts
    properties: {
      plan: currentUser.plan || 'free',
      role: currentUser.role || 'user',
      region: currentUser.region || 'us-east',
      signupDate: currentUser.createdAt,
    }
  }}
  isPending={isLoadingSession}
  debug={process.env.NODE_ENV === 'development'}
  autoFetch={true}
  cacheTtl={60000}        // Cache for 1 minute
  staleTime={30000}       // Revalidate after 30s
>
  <App />
</FlagsProvider>

Configuration Options

OptionTypeDefaultDescription
clientIdstringRequiredYour website ID from the dashboard
apiUrlstringhttps://api.databuddy.ccAPI endpoint
userobjectundefinedUser context for targeting
isPendingbooleanfalseDefer evaluation while session loads
disabledbooleanfalseDisable all flag evaluation
debugbooleanfalseEnable debug logging
environmentstringundefinedEnvironment name
cacheTtlnumber60000Cache TTL in ms (1 minute)
staleTimenumber30000Revalidate after (30 seconds)
autoFetchbooleantrueAuto-fetch flags on mount
skipStoragebooleanfalseSkip localStorage caching

User Context Options

OptionTypeDescription
userIdstringUnique user identifier for per-user rollouts
emailstringUser email (alternative identifier)
organizationIdstringOrganization ID for org-level rollouts
teamIdstringTeam ID for team-level rollouts
propertiesobjectCustom properties for targeting rules

Advanced Context API

Access the flags context directly for more control:

tsx
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' }
    });
  };
}

Flag States

The FlagState object has the following properties:

tsx
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

Stale-While-Revalidate

Flags return cached values immediately while revalidating in the background.

tsx
// 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.

tsx
// These 3 calls become 1 API request
const feature1 = useFeature('feature-1');
const feature2 = useFeature('feature-2');
const feature3 = useFeature('feature-3');

Visibility API

Pauses fetching when tab is hidden to save bandwidth and battery.

Request Deduplication

Identical requests are deduplicated automatically.

Why isPending Matters

The isPending prop prevents race conditions during authentication:

tsx
// ❌ Bad: Flags evaluate with wrong user context
<FlagsProvider user={undefined}>
  <App /> // Shows anonymous features, then switches
</FlagsProvider>

// ✅ Good: Waits for session before evaluating
<FlagsProvider 
  isPending={isPending} 
  user={session?.user ? {...} : undefined}
>
  <App /> // Shows correct features immediately
</FlagsProvider>

Benefits:

  • Prevents flash of incorrect content
  • Avoids unnecessary API calls
  • Consistent user experience
  • Better performance

User Targeting

Target specific users or groups from your dashboard:

  • User ID: Target specific users by their unique identifier
  • Email: Target by email address or domain (e.g., @company.com)
  • Organization: Roll out to entire organizations at once
  • Team: Roll out to specific teams
  • Custom Properties: Target by user attributes

Organization & Team Rollouts

For enterprise features or billing changes that should apply to all members of an organization:

tsx
<FlagsProvider
  user={{
    userId: "user-123",
    email: "user@company.com",
    organizationId: "org_abc",  // All org members get same result
    teamId: "team_456",         // All team members get same result
  }}
>

Then in the dashboard, set the Rollout Unit to "Organization" or "Team" when configuring your rollout flag.

Advanced Targeting with Custom Properties

tsx
<FlagsProvider
  user={{
    userId: "user-123",
    email: "user@example.com",
    organizationId: "org_abc",
    teamId: "team_456",
    properties: {
      plan: 'premium',           // Plan-based rollouts
      region: 'us-east',         // Geographic targeting
      signupDate: '2024-01-01',  // Time-based targeting
      experimentGroup: 'A',      // A/B testing
      featureUsage: {
        reportsViewed: 25        // Behavioral targeting
      }
    }
  }}
>

Targeting Examples:

  • Plan-based: plan: 'premium' → Show premium features
  • Geographic: region: 'us-east' → Test in specific regions
  • Behavioral: reportsViewed > 10 → Target power users
  • Time-based: signupDate > '2024-01-01' → New vs. existing users

Best Practices

✅ Use the Right Hook

tsx
// Simple boolean check
const { on, loading } = useFeature('dark-mode');

// SSR-safe with default
const isDarkMode = useFeatureOn('dark-mode', false);

// Full control with status
const flag = useFlag('experiment');
if (flag.status === 'ready') { ... }

// Typed values
const maxItems = useFlagValue('max-items', 10);

// A/B test variants
const variant = useVariant('checkout-test');

✅ Handle Loading States

tsx
function FeatureComponent() {
  const { on, loading } = useFeature('new-feature');
  
  if (loading) return <Skeleton />;
  return on ? <NewFeature /> : <OldFeature />;
}

✅ Multiple Flags

tsx
function Dashboard() {
  const { on: darkMode } = useFeature('dark-mode');
  const { on: newLayout } = useFeature('new-layout');
  const maxItems = useFlagValue('max-items', 10);

  return (
    <div className={darkMode ? 'dark' : ''}>
      {newLayout ? <NewLayout items={maxItems} /> : <OldLayout />}
    </div>
  );
}

✅ Update User Context

tsx
function UpgradeButton() {
  const { updateUser } = useFlags();

  const handleUpgrade = () => {
    // Update user context to refresh flags
    updateUser({
      userId: user.id,
      email: user.email,
      properties: { plan: 'premium' }
    });
  };

  return <button onClick={handleUpgrade}>Upgrade</button>;
}

Migration from Old API

tsx
// ❌ Old (confusing)
const { isEnabled } = useFlags();
const flag = isEnabled('my-feature');
if (flag.isReady && flag.enabled) {
  return <NewFeature />;
}

// ✅ New (clear)
const { on, loading } = useFeature('my-feature');
if (loading) return <Skeleton />;
return on ? <NewFeature /> : <OldFeature />;

// Or even simpler for SSR
const isOn = useFeatureOn('my-feature', false);
return isOn ? <NewFeature /> : <OldFeature />;

The old API (isEnabled) is still supported for backwards compatibility but deprecated.

Debug Mode

Enable debug mode to see flag evaluation in the console:

tsx
<FlagsProvider
  debug={true}
  // ... other props
>

Ready to get started? Create your first feature flag in the dashboard →

How is this guide?