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, useFlag } 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 } = useFlag('dark-mode');
  const { on: showNewDashboard } = useFlag('new-dashboard');

  if (loading) return <Skeleton />;

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

Hooks API

useFlag(key) - Flag State

The primary hook for checking feature flags. Returns { on, loading, status, value, variant }.

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

function MyComponent() {
  const { on, loading } = useFlag('new-dashboard');

  if (loading) return <Skeleton />;
  return on ? <NewDashboard /> : <OldDashboard />;
}

You can also use the status field for more granular control:

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 />;
  }
}

useFlags() - Context API

Access the full flags context for advanced use cases like checking multiple flags, fetching typed values, or updating user context.

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 boolean check
  if (isOn('premium-feature')) {
    // Show premium content
  }

  // Typed values (string, number, boolean)
  const maxItems = getValue<number>('max-items', 10);
  const theme = getValue<'light' | 'dark'>('theme', 'light');

  // Get full state
  const flag = getFlag('experiment');
  console.log(flag.on, flag.loading, flag.status);

  // A/B test variants
  const variant = getFlag('checkout-experiment').variant;
}

Flag Types

Boolean Flags

Simple on/off switches for features.

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

String/Number Flags

For configuration values and multivariate testing. Use getValue from the context:

tsx
const { getValue } = useFlags();
const maxUsers = getValue<number>('max-users', 100);
const theme = getValue<'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 } = useFlag('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. Use the variant field from useFlag:

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

function CheckoutPage() {
  const { variant, loading } = useFlag('checkout-experiment');

  if (loading) return <Skeleton />;

  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

Flag States

The FlagState object returned by useFlag:

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
}

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 } = useFlag('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 = useFlag('feature-1');
const feature2 = useFlag('feature-2');
const feature3 = useFlag('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 API

tsx
// Simple boolean check — most common
const { on, loading } = useFlag('dark-mode');

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

// A/B test variants
const { variant } = useFlag('checkout-test');

// Typed values via context
const { getValue } = useFlags();
const maxItems = getValue<number>('max-items', 10);

// Quick boolean via context (no loading state)
const { isOn } = useFlags();
if (isOn('premium')) { ... }

Handle Loading States

tsx
function FeatureComponent() {
  const { on, loading } = useFlag('new-feature');

  if (loading) return <Skeleton />;
  return on ? <NewFeature /> : <OldFeature />;
}

Multiple Flags

tsx
function Dashboard() {
  const { on: darkMode } = useFlag('dark-mode');
  const { on: newLayout } = useFlag('new-layout');
  const { getValue } = useFlags();
  const maxItems = getValue<number>('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>;
}

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?