@savvagent/react
v1.0.1
Published
React SDK for Savvagent feature flags
Maintainers
Readme
@savvagent/react
React SDK for Savvagent - AI-powered feature flags that prevent production incidents.
Installation
npm install @savvagent/react
# or
pnpm add @savvagent/react
# or
yarn add @savvagent/reactQuick Start
1. Wrap your app with SavvagentProvider
import { SavvagentProvider } from '@savvagent/react';
function App() {
return (
<SavvagentProvider
config={{
apiKey: 'sdk_your_api_key_here',
applicationId: 'your-app-id', // Optional: for application-scoped flags
enableRealtime: true, // Enable real-time flag updates
}}
>
<YourApp />
</SavvagentProvider>
);
}2. Use the useFlag hook
import { useFlag } from '@savvagent/react';
function MyFeature() {
const { value: isEnabled, loading } = useFlag('new-checkout-flow', {
defaultValue: false,
realtime: true,
});
if (loading) {
return <div>Loading...</div>;
}
return isEnabled ? <NewCheckout /> : <OldCheckout />;
}API Reference
<SavvagentProvider>
Provider component that initializes the Savvagent client and makes it available to all child components.
Props
interface SavvagentProviderProps {
config: FlagClientConfig;
children: React.ReactNode;
}
interface FlagClientConfig {
/** SDK API key (starts with sdk_) */
apiKey: string;
/** Application ID for application-scoped flags */
applicationId?: string;
/** Base URL for the Savvagent API */
baseUrl?: string;
/** Enable real-time flag updates via SSE (default: true) */
enableRealtime?: boolean;
/** Cache TTL in milliseconds (default: 60000) */
cacheTtl?: number;
/** Enable telemetry tracking (default: true) */
enableTelemetry?: boolean;
/** Default flag values when evaluation fails */
defaults?: Record<string, boolean>;
/** Custom error handler */
onError?: (error: Error) => void;
/** Default language for targeting */
defaultLanguage?: string;
/** Disable automatic browser language detection */
disableLanguageDetection?: boolean;
}useFlag(flagKey, options)
Hook to evaluate a feature flag with automatic updates.
Parameters
flagKey: string- The feature flag key to evaluateoptions?: UseFlagOptions- Configuration options
interface UseFlagOptions {
/** Context for flag evaluation (user_id, attributes, etc.) */
context?: FlagContext;
/** Default value to use while loading or on error */
defaultValue?: boolean;
/** Enable real-time updates for this flag (default: true) */
realtime?: boolean;
/** Custom error handler */
onError?: (error: Error) => void;
}Returns
interface UseFlagResult {
/** Current flag value */
value: boolean;
/** Whether the flag is currently being evaluated */
loading: boolean;
/** Error if evaluation failed */
error: Error | null;
/** Detailed evaluation result */
result: FlagEvaluationResult | null;
/** Force re-evaluation of the flag */
refetch: () => Promise<void>;
}Example
function MyComponent() {
const { value, loading, error, refetch } = useFlag('beta-feature', {
context: {
user_id: user?.id,
attributes: {
plan: user?.plan,
country: user?.country,
},
},
defaultValue: false,
realtime: true,
onError: (err) => console.error('Flag evaluation failed:', err),
});
if (loading) return <Spinner />;
if (error) return <ErrorMessage error={error} />;
return (
<div>
{value ? <BetaFeature /> : <StandardFeature />}
<button onClick={() => refetch()}>Refresh Flag</button>
</div>
);
}useSavvagent()
Hook to access the underlying Savvagent client instance for advanced use cases.
Returns
interface SavvagentContextValue {
/** The FlagClient instance */
client: FlagClient | null;
/** Whether the client is ready */
isReady: boolean;
}Example
function AdvancedComponent() {
const { client, isReady } = useSavvagent();
useEffect(() => {
if (!isReady || !client) return;
// Use client directly
const checkMultipleFlags = async () => {
const flag1 = await client.isEnabled('feature-1');
const flag2 = await client.isEnabled('feature-2');
console.log({ flag1, flag2 });
};
checkMultipleFlags();
}, [client, isReady]);
return <div>Content</div>;
}useWithFlag(flagKey, callback, options)
Hook to execute a callback conditionally based on a flag value.
Example
function AnalyticsComponent() {
useWithFlag('analytics-enabled', async () => {
await trackEvent('page_view', {
page: window.location.pathname,
});
});
return <PageContent />;
}useUser()
Hook to manage user identification for targeted flag evaluation.
Returns
{
setUserId: (userId: string | null) => void;
getUserId: () => string | null;
getAnonymousId: () => string | null;
setAnonymousId: (id: string) => void;
}Example
function AuthHandler() {
const { setUserId } = useUser();
useEffect(() => {
if (user) {
setUserId(user.id);
} else {
setUserId(null); // Clear on logout
}
}, [user, setUserId]);
return null;
}useTrackError(flagKey, context)
Hook to track errors with flag context for AI-powered error analysis.
Example
function FeatureComponent() {
const trackError = useTrackError('new-payment-flow');
const [result, setResult] = useState(null);
const handlePayment = async () => {
try {
const result = await processPayment();
setResult(result);
} catch (error) {
trackError(error as Error);
toast.error('Payment failed');
}
};
return <button onClick={handlePayment}>Pay Now</button>;
}Advanced Examples
User Targeting
function UserSpecificFeature() {
const user = useCurrentUser();
const { value: showPremium } = useFlag('premium-features', {
context: {
user_id: user.id,
attributes: {
plan: user.subscription.plan,
signupDate: user.createdAt,
},
},
});
return showPremium ? <PremiumDashboard /> : <StandardDashboard />;
}Language Targeting
function LocalizedFeature() {
const { i18n } = useTranslation();
const { value: showLocalFeature } = useFlag('regional-promotion', {
context: {
language: i18n.language, // e.g., 'en-US', 'fr-FR'
},
});
return showLocalFeature ? <RegionalPromo /> : null;
}A/B Testing
function ABTest() {
const user = useCurrentUser();
const { value: variantB } = useFlag('checkout-variant-b', {
context: {
user_id: user.id, // Consistent assignment per user
},
});
return variantB ? <CheckoutVariantB /> : <CheckoutVariantA />;
}Error Tracking
function ExperimentalFeature() {
const trackError = useTrackError('experimental-algorithm');
const handleComputation = async () => {
try {
const result = await experimentalComputation();
return result;
} catch (error) {
// Error is automatically correlated with flag changes
trackError(error as Error);
throw error;
}
};
return <ComputationUI onCompute={handleComputation} />;
}TypeScript Support
This package is written in TypeScript and provides full type definitions.
import type {
FlagClientConfig,
FlagContext,
FlagEvaluationResult,
UseFlagOptions,
UseFlagResult,
} from '@savvagent/react';Best Practices
Place SavvagentProvider high in your component tree to ensure all components have access to the client.
Use the
defaultValueoption to provide a safe fallback while flags are loading.Enable real-time updates for flags that change frequently or require immediate propagation.
Track errors in new features to leverage Savvagent's AI-powered error correlation.
Use user context for targeted rollouts based on user attributes, location, or behavior.
Handle loading states gracefully to provide a smooth user experience.
License
MIT
