Skip to Content
Developer GuideReactReact

React

A React SDK for Supaship that provides hooks and components for feature flag management with full TypeScript type safety.

Requirements

Installation

pnpm add @supashiphq/react-sdk

Quick Start

import { SupaProvider, useFeature, FeaturesWithFallbacks, } from '@supashiphq/react-sdk' // Define your features with type safety const FEATURE_FLAGS = { 'new-header': false, 'theme-config': { mode: 'dark' as 'dark' | 'light', showLogo: true }, 'beta-features': [] as string[], } satisfies FeaturesWithFallbacks function App() { return ( <SupaProvider config={{ apiKey: 'your-api-key', environment: 'production', features: FEATURE_FLAGS, context: { userID: '123', email: '[email protected]', }, }}> <YourApp /> </SupaProvider> ) } function YourApp() { // Hook returns { feature, isLoading, error, ... } const { feature: newHeader, isLoading } = useFeature('new-header') if (isLoading) return <div>Loading...</div> return <div>{newHeader ? <NewHeader /> : <OldHeader />}</div> }

Type-Safe Feature Flags

For full TypeScript type safety, define your features and augment the Features interface:

// lib/features.ts import { FeaturesWithFallbacks, InferFeatures } from '@supashiphq/react-sdk' export const FEATURE_FLAGS = { 'new-header': false, 'theme-config': { mode: 'dark' as 'dark' | 'light', primaryColor: '#007bff', showLogo: true, }, 'beta-features': [] as string[], 'disabled-feature': null, } satisfies FeaturesWithFallbacks // Type augmentation for global type safety, it is required declare module '@supashiphq/react-sdk' { interface Features extends InferFeatures<typeof FEATURE_FLAGS> {} }

Now useFeature and useFeatures will have full type safety:

function MyComponent() { // TypeScript knows 'new-header' is valid and feature is boolean | null const { feature } = useFeature('new-header') // TypeScript knows 'theme-config' returns the exact object shape const { feature: config } = useFeature('theme-config') // config is { mode: 'dark' | 'light', primaryColor: string, showLogo: boolean } | null // TypeScript will error on invalid feature names const { feature: invalid } = useFeature('non-existent-feature') // ❌ Type error }

API Reference

For complete API documentation, see the React SDK API Reference.

Best Practices

1. Use Type Augmentation for Type Safety

// ✅ Good - type augmentation for global type safety declare module '@supashiphq/react-sdk' { interface Features extends InferFeatures<typeof FEATURE_FLAGS> {} } // Now all useFeature calls are type-safe const { feature } = useFeature('new-header') // ✅ TypeScript knows this is boolean | null const { feature } = useFeature('invalid') // ❌ TypeScript error

2. Use Context for User Targeting

function App() { const { user } = useAuth() return ( <SupaProvider config={{ apiKey: 'your-api-key', features: FEATURE_FLAGS, context: { userId: user?.id, email: user?.email, plan: user?.subscriptionPlan, version: process.env.REACT_APP_VERSION, }, }}> <YourApp /> </SupaProvider> ) }

3. Batch Feature Requests

// ✅ Good - single API call const { features } = useFeatures(['feature-1', 'feature-2', 'feature-3']) // ❌ Less efficient - multiple API calls const feature1 = useFeature('feature-1') const feature2 = useFeature('feature-2') const feature3 = useFeature('feature-3')

4. Handle Loading States

function MyComponent() { const { user, isLoading: userLoading } = useUser() const { features, isLoading: featuresLoading } = useFeatures( ['user-specific-feature'], { context: { userId: user?.id }, shouldFetch: !userLoading && !!user, }, ) if (userLoading || featuresLoading) return <Skeleton /> return <div>{features['user-specific-feature'] && <SpecialContent />}</div> }

5. Update Context Reactively

function UserDashboard() { const { updateContext } = useFeatureContext() const [currentPage, setCurrentPage] = useState('dashboard') // Update context when navigation changes useEffect(() => { updateContext({ currentPage }) }, [currentPage, updateContext]) return ( <div> <Navigation onPageChange={setCurrentPage} /> <PageContent page={currentPage} /> </div> ) }

Framework Integration

Next.js App Router (Next.js 13+)

// app/providers.tsx 'use client' import { SupaProvider, FeaturesWithFallbacks } from '@supashiphq/react-sdk' const FEATURE_FLAGS = { 'new-hero': false, theme: { mode: 'light' as 'light' | 'dark' }, } satisfies FeaturesWithFallbacks export function Providers({ children }: { children: React.ReactNode }) { return ( <SupaProvider config={{ apiKey: process.env.NEXT_PUBLIC_SUPASHIP_API_KEY!, environment: process.env.NODE_ENV!, features: FEATURE_FLAGS, }}> {children} </SupaProvider> ) } // app/layout.tsx import { Providers } from './providers' export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( <html lang='en'> <body> <Providers>{children}</Providers> </body> </html> ) } // app/page.tsx ;('use client') import { useFeature } from '@supashiphq/react-sdk' export default function HomePage() { const { feature: newHero } = useFeature('new-hero') return <main>{newHero ? <NewHeroSection /> : <OldHeroSection />}</main> }

Next.js Pages Router (Next.js 12 and below)

// pages/_app.tsx import { SupaProvider, FeaturesWithFallbacks } from '@supashiphq/react-sdk' import type { AppProps } from 'next/app' const FEATURE_FLAGS = { 'new-homepage': false, } satisfies FeaturesWithFallbacks export default function App({ Component, pageProps }: AppProps) { return ( <SupaProvider config={{ apiKey: process.env.NEXT_PUBLIC_SUPASHIP_API_KEY!, environment: process.env.NODE_ENV!, features: FEATURE_FLAGS, }}> <Component {...pageProps} /> </SupaProvider> ) }

Vite / Create React App

// src/main.tsx or src/index.tsx import { SupaProvider, FeaturesWithFallbacks } from '@supashiphq/react-sdk' const FEATURE_FLAGS = { 'new-ui': false, theme: { mode: 'light' as 'light' | 'dark' }, } satisfies FeaturesWithFallbacks function App() { return ( <SupaProvider config={{ apiKey: import.meta.env.VITE_SUPASHIP_API_KEY, // Vite // or apiKey: process.env.REACT_APP_SUPASHIP_API_KEY, // CRA environment: import.meta.env.MODE, features, }}> <YourApp /> </SupaProvider> ) }

Development Toolbar

The SDK includes a development toolbar for testing and debugging feature flags locally.

<SupaProvider config={{ ... }} toolbar={{ show: 'auto', // 'auto' | 'always' | 'never' position: 'bottom-right', // 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' }} > <YourApp /> </SupaProvider>
  • 'auto': Shows toolbar in development environments only (default)
  • 'always': Always shows toolbar
  • 'never': Never shows toolbar

The toolbar allows you to:

  • View all available feature flags
  • Override feature values locally
  • See feature value types and current values
  • Clear local overrides

Plugins

Extend the SDK functionality with custom plugins:

import { SupaPlugin } from '@supashiphq/react-sdk' const analyticsPlugin: SupaPlugin = { name: 'analytics', onInit: async (availableFeatures, context) => { console.log('SDK initialized with features:', Object.keys(availableFeatures)) }, afterGetFeatures: async (features, context) => { // Track feature usage analytics.track('features_fetched', { features: Object.keys(features) }) }, } <SupaProvider config={config} plugins={[analyticsPlugin]}> <App /> </SupaProvider>

Testing

Mocking Feature Flags in Tests

// test-utils/providers.tsx import { SupaProvider, FeaturesWithFallbacks } from '@supashiphq/react-sdk' export function TestProviders({ children, features = {} }) { const testFeatures = { ...features } satisfies FeaturesWithFallbacks return ( <SupaProvider config={{ apiKey: 'test-key', environment: 'test', features: { ...features } satisfies FeaturesWithFallbacks, }}> {children} </SupaProvider> ) }

Example Test

// MyComponent.test.tsx import { render, screen } from '@testing-library/react' import { TestProviders } from '../test-utils/providers' import MyComponent from './MyComponent' describe('MyComponent', () => { it('shows new feature when enabled', () => { render( <TestProviders features={{ 'new-feature': true }}> <MyComponent /> </TestProviders>, ) expect(screen.getByText('New Feature Content')).toBeInTheDocument() }) it('shows old feature when disabled', () => { render( <TestProviders features={{ 'new-feature': false }}> <MyComponent /> </TestProviders>, ) expect(screen.getByText('Old Feature Content')).toBeInTheDocument() }) })

Troubleshooting

Common Issues

Provider Not Found Error

Error: useFeature must be used within a SupaProvider

Solution: Ensure your component is wrapped in a SupaProvider:

// ✅ Correct function App() { return ( <SupaProvider config={{ ... }}> <MyComponent /> </SupaProvider> ) } // ❌ Incorrect function App() { return <MyComponent /> // Missing provider }

Features Not Loading

  • Check API key: Verify your API key is correct
  • Check network: Open browser dev tools and check network requests
  • Check features config: Ensure features are defined in the config

Type Errors

Property 'my-feature' does not exist on type 'Features'

Solution: Add type augmentation:

import { SupaFeatures } from '@supashiphq/react-sdk' import { FEATURE_FLAGS } from './features' declare module '@supashiphq/react-sdk' { interface Features extends SupaFeatures<typeof FEATURE_FLAGS> {} }
Last updated on