npm package: @bannerflex/react-native
Description: React Native and Expo components and API helpers for BannerFlex
Latest version: 0.1.0

# @bannerflex/react-native

React Native and Expo components and API helpers for [BannerFlex](https://www.bannerflex.app). Add BannerFlex-powered popups and analytics to any React Native or Expo app.

## Install

```bash
npm install @bannerflex/react-native @react-native-async-storage/async-storage
# or
yarn add @bannerflex/react-native @react-native-async-storage/async-storage
pnpm add @bannerflex/react-native @react-native-async-storage/async-storage
```

With Expo, AsyncStorage is usually already installed. If not:

```bash
npx expo install @react-native-async-storage/async-storage
```

## Quick start: Popup

1. **Wrap your app** (optional but recommended):

```tsx
import { BannerFlexProvider, BannerFlexPopup } from '@bannerflex/react-native';
import { View, Text, Pressable } from 'react-native';

function App() {
  return (
    <BannerFlexProvider baseUrl="https://www.bannerflex.app">
      <YourApp />
      <BannerFlexPopup
        bannerId="your-banner-id-from-dashboard"
        campaignId="bnrflx-your-campaign-uuid"
        delay={2}
        frequencyToShow={24}
        dismissOnBackdropPress
        renderCloseButton={(onClose) => (
          <Pressable onPress={onClose} accessibilityLabel="Close">
            <Text>×</Text>
          </Pressable>
        )}
      >
        <View style={{ padding: 24, backgroundColor: 'white', borderRadius: 8 }}>
          <Text>Welcome</Text>
          <Text>This popup is powered by BannerFlex.</Text>
        </View>
      </BannerFlexPopup>
    </BannerFlexProvider>
  );
}
```

2. **Without a provider** — pass `baseUrl` on the component:

```tsx
<BannerFlexPopup
  baseUrl="https://www.bannerflex.app"
  bannerId="your-banner-id"
  campaignId="bnrflx-xxx"
  delay={0}
  displayOnDelay
  frequencyToShow={24}
  dismissOnBackdropPress
  renderCloseButton={(onClose) => <Pressable onPress={onClose}><Text>Close</Text></Pressable>}
>
  <View><Text>Your content</Text></View>
</BannerFlexPopup>
```

## Popup props

| Prop | Type | Default | Description |
|------|------|--------|-------------|
| `bannerId` | `string` | required | Banner ID from the BannerFlex dashboard. |
| `campaignId` | `string` | `''` | Campaign ID (e.g. `bnrflx-xxx`). Use a deep link with `?campaign=bnrflx-xxx` to preview. |
| `baseUrl` | `string` | from provider or `https://www.bannerflex.app` | API base URL. |
| `delay` | `number` | `0` | Seconds before auto-showing. |
| `displayOnDelay` | `boolean` | `true` | If `false`, the popup only shows when you call `ref.current.open()`. |
| `frequencyToShow` | `number` | `24` | Show again after this many hours (since last view). `0` = every time. |
| `lastTimeUpdated` | `string` | `''` | ISO date; if content was updated after the user's last visit, show again. |
| `isScheduled` | `boolean` | `false` | When `true`, only show between `startDate` and `endDate`. |
| `startDate` / `endDate` | `string` | `''` | ISO date range when `isScheduled` is true. |
| `backgroundColor` | `string` | `'rgba(0,0,0,0.5)'` | Backdrop overlay color. |
| `dismissOnBackdropPress` | `boolean` | `false` | Allow closing by pressing the backdrop. |
| `renderCloseButton` | `(onClose) => ReactNode` | — | Optional close button. |
| `onClose` | `() => void` | — | Called when the popup closes. |
| `contentStyle` | `ViewStyle` | — | Optional style for the popup content wrapper. |

## Programmatic open/close

Use a ref when `displayOnDelay={false}`:

```tsx
import { useRef } from 'react';
import { BannerFlexPopup, type BannerFlexPopupRef } from '@bannerflex/react-native';
import { Pressable, Text } from 'react-native';

function MyScreen() {
  const popupRef = useRef<BannerFlexPopupRef>(null);

  return (
    <>
      <Pressable onPress={() => popupRef.current?.open()}>
        <Text>Show popup</Text>
      </Pressable>
      <BannerFlexPopup
        ref={popupRef}
        bannerId="your-banner-id"
        campaignId="bnrflx-xxx"
        displayOnDelay={false}
        renderCloseButton={(onClose) => <Pressable onPress={onClose}><Text>Close</Text></Pressable>}
      >
        <View><Text>Popup content</Text></View>
      </BannerFlexPopup>
    </>
  );
}
```

## Tracking CTA and custom metrics

Inside the popup content, use the hook to send CTA clicks and custom metrics:

```tsx
import { useBannerFlexPopupTrack } from '@bannerflex/react-native';
import { Pressable, Text } from 'react-native';

function PopupContent() {
  const track = useBannerFlexPopupTrack();

  return (
    <View>
      <Pressable
        onPress={() => {
          track?.trackCta();
          // navigate or other action
        }}
      >
        <Text>Sign up</Text>
      </Pressable>
      <Pressable
        onPress={() => {
          track?.trackMetric('metric-uuid-from-dashboard');
          // custom action
        }}
      >
        <Text>Custom action</Text>
      </Pressable>
    </View>
  );
}
```

## Deep links and test links

Campaign and country from **deep links** (e.g. `myapp://screen?campaign=bnrflx-xxx&country=GB`) are read automatically via `useBannerFlexLinking()`, which the Popup uses internally. So test links from the BannerFlex dashboard work when you open the app via that URL.

You can also use the URL helpers directly if you have a URL string (e.g. from `Linking.getInitialURL()`):

```ts
import {
  getCampaignFromUrl,
  getCountryFromUrl,
  shouldShowBanner,
  sendBannerEvent,
  isWithinSchedule,
} from '@bannerflex/react-native';
import { Linking } from 'react-native';

const url = await Linking.getInitialURL();
const campaign = getCampaignFromUrl(url);
const country = getCountryFromUrl(url);

const show = await shouldShowBanner(
  'https://www.bannerflex.app',
  'banner-id',
  'bnrflx-campaign-id',
  country ?? undefined
);

sendBannerEvent('https://www.bannerflex.app', 'banner-id', 'impression', {
  campaignId: campaign ?? undefined,
});

const inRange = isWithinSchedule('2025-01-01', '2025-12-31');
```

## API summary

- **`shouldShowBanner(baseUrl, bannerId, campaignId, countryOverride?)`** — async; returns whether to show (e.g. geo rules).
- **`sendBannerEvent(baseUrl, bannerId, event, options?)`** — send `impression`, `click`, or `custom` (with `metricId`).
- **`getCampaignFromUrl(url)`** — parse `?campaign=` from a URL string (e.g. from Linking).
- **`getCountryFromUrl(url)`** — parse `?country=` from a URL string.
- **`isWithinSchedule(startDate, endDate)`** — true if current time is in range.
- **`useBannerFlexLinking()`** — returns `{ url, campaignId, country }` from the app URL / deep link.

## License

MIT