@supashiphq/react-native-sdk
v1.1.0
Published
Supaship SDK for React Native
Readme
Supaship React Native SDK
Supaship SDK for React Native: same hooks and provider model as @supashiphq/react-sdk.
Installation
npm install @supashiphq/react-native-sdkPeer dependencies: react, react-native (≥ 0.71). The core HTTP client ships inside @supashiphq/javascript-sdk via this package’s dependencies.
Quick start
import { SupashipProvider, useFeature, FeaturesWithFallbacks } from '@supashiphq/react-native-sdk'
import { View, Text } from 'react-native'
const features = {
'new-header': false,
'theme-config': { mode: 'dark' as const, showLogo: true },
} satisfies FeaturesWithFallbacks
export function App() {
return (
<SupashipProvider
config={{
sdkKey: 'your-sdk-key',
environment: 'production',
features,
context: { userId: '123' },
toolbar: false,
}}
>
<Home />
</SupashipProvider>
)
}
function Home() {
const { feature: newHeader, isLoading } = useFeature('new-header')
if (isLoading) return <Text>Loading…</Text>
return <View>{newHeader ? <Text>New</Text> : <Text>Old</Text>}</View>
}Use toolbar: false in native apps (the dev toolbar targets browsers only).
Type-safe feature flags
Declare flags once, then augment this package so hooks stay typed:
// features.ts
import { FeaturesWithFallbacks, InferFeatures } from '@supashiphq/react-native-sdk'
export const FEATURE_FLAGS = {
'new-header': false,
'theme-config': { mode: 'dark' as const, showLogo: true },
} satisfies FeaturesWithFallbacks
declare module '@supashiphq/react-native-sdk' {
interface Features extends InferFeatures<typeof FEATURE_FLAGS> {}
}After that, useFeature('new-header') and useFeature('theme-config') infer correct value types. Invalid keys are a TypeScript error.
Use cases
Several flags in one request
import { useFeatures } from '@supashiphq/react-native-sdk'
function Dashboard() {
const { features, isLoading } = useFeatures(['new-header', 'paywall-v2'] as const, {
context: { plan: 'pro' },
})
if (isLoading) return <Text>…</Text>
return <Text>{features['paywall-v2'] ? 'Paywall B' : 'Paywall A'}</Text>
}Declarative boolean gate (SupashipFeature)
import { SupashipFeature } from '@supashiphq/react-native-sdk'
import { View, Text } from 'react-native'
function Header() {
return (
<SupashipFeature
feature="new-header"
loading={<Text>…</Text>}
variations={{
true: <Text>New header</Text>,
false: <Text>Legacy header</Text>,
}}
/>
)
}Children are normal ReactNode values (View, Text, etc.).
Update targeting context after login
import { useFeatureContext } from '@supashiphq/react-native-sdk'
import { useEffect } from 'react'
function SessionBridge({ userId }: { userId: string | undefined }) {
const { updateContext } = useFeatureContext()
useEffect(() => {
if (userId) updateContext({ userId })
}, [userId, updateContext])
return null
}Merging behavior matches the web SDK (mergeWithExisting defaults to true).
Advanced: useClient and useQuery
import { useClient, useQuery } from '@supashiphq/react-native-sdk'
function RemoteConfig() {
const client = useClient()
const { data, isLoading } = useQuery(['remote-config'], () =>
client.getFeature('theme-config').then(theme => ({ theme }))
)
if (isLoading || !data) return null
return <Text>{JSON.stringify(data.theme)}</Text>
}refetchOnWindowFocus on useQuery still uses that name for API parity; on native it refetches when the app becomes active again.
refetchOnWindowFocus and AppState
| Platform | Trigger for refetch (when option is true) |
| -------- | ------------------------------------------- |
| Web SDK | Browser visibilitychange / window focus |
| Native | AppState transitions to active |
Defaults for useFeature / useFeatures keep network minimal (refetchOnWindowFocus: off unless you opt in).
Sensitive context hashing
If you set sensitiveContextProperties, hashing uses Web Crypto where available. Some Hermes setups need a crypto.subtle polyfill; see @supashiphq/javascript-sdk for details.
Unit testing in your app
Same idea as the React SDK: wrap in SupashipProvider with test config and toolbar={false}, then render your screen.
If Jest resolves react-native from this package’s internals, add a tiny stub so tests do not need the full native binary:
// jest.config.js
module.exports = {
moduleNameMapper: {
'^react-native$': '<rootDir>/test/react-native-stub.js',
},
}// test/react-native-stub.js
exports.AppState = {
addEventListener() {
return { remove() {} }
},
}License
MIT — see LICENSE.
