# React Native Embedding

Learn how to embed Komo experiences in your React Native application.

Komo cards can be embedded into your React Native application through Komo's NPM package [@komo-tech/react-native-widgets](https://www.npmjs.com/package/@komo-tech/react-native-widgets).

## Installation

```bash
npm install @komo-tech/react-native-widgets
```

> **Note**
>
> [@komo-tech/react-native-widgets](https://www.npmjs.com/package/@komo-tech/react-native-widgets)
> has a peer dependency on
> [react-native-webview](https://github.com/react-native-webview/react-native-webview),
> and recommends the latest major version, `13.x`.

## Basic Usage

- The quickest way to get started with embedding Komo content in react native is by using the `KomoCardWidget` component.
- This component combines metadata fetching, card cover display, and modal handling for the card experience.
- The only required prop is `embedMetaUrl`. To find this in the Komo portal:
  - Navigate to the settings of the card to be embedded.
  - Select the `Embed` tab and click on `React Native code` in the right sidebar.
  - Copy the `Card embed meta URL` and use it as the value of the `embedMetaUrl` prop.

```js

// ...

<KomoCardWidget
  embedMetaUrl={KomoCardNativeEmbedUrl}
  containerStyle={{ maxWidth: '80%' }}
/>;
```

### Prefilling form details

- You can pass information through to the Komo experience that will be pre-filled in any forms that the user may encounter.
- Pass a plain `Record<string,string>` object of keys and values through to the `formPrefillValues` prop on `KomoCardWidget` or `ExperienceModal`.
- The object keys must match the Unique ID of the form field or contact property from the Komo Platform that you want to prefill.

```js
<KomoCardWidget
  embedMetaUrl={KomoCardNativeEmbedUrl}
  containerStyle={{ maxWidth: '80%' }}
  formPrefillValues={{
    email: 'email@domain.com',
    first_name: 'Person',
    last_name: 'Doe'
  }}
/>
```

## Advanced usage

### Metadata fetching

- The first step to using embedded Komo content involves fetching the card metadata.
- Use the `useFetchCardMetadata` hook and the `Native embed URL` copied from the platform to fetch the `CardEmbedMetadata`.
- The `CardEmbedMetadata` has the information required to render the cover image (`imageUrl`) and the URL (`embedUrl`) that the `ExperienceModal` needs to render the embedded experience.
- Note: you can use your own data-fetching patterns if you require more advanced data fetching handling. So long as it produces a `CardEmbedMetadata`, you can pass that to the other components that you want to use.

```js

// ... rest of your component

const { data, isLoading, isError } = useFetchCardMetadata({
  embedMetaUrl: KomoCardNativeEmbedUrl
});

// ... use the data.
```

### Render a Card Cover

- The `CardCover` component is used to display the cover image of a Komo card.
- It handles loading states, error states, and button display.
- The component requires an `onClick` handler and `isLoading` state.
- The `imageUrl` and `imageAspectRatio` props are typically obtained from the `CardEmbedMetadata`.

```js

// ... rest of your component

<CardCover
  imageUrl={metadata?.imageUrl}
  imageAspectRatio={metadata?.imageAspectRatio}
  isLoading={isLoading}
  isError={isError}
  onClick={() => doSomethingOnCoverClicked()}
  metaButtonStyle={metadata?.buttonStyle}
  containerStyle={{ borderRadius: 8 }}
/>;
```

### Using the Experience Modal

- The `ExperienceModal` component is used to display the full Komo experience in a modal overlay.
- It handles loading states, error states, and communication with the embedded experience.
- The component requires an `isOpen` state and `onClose` handler.
- A valid `embedUrl` prop is required for the experience modal to function, and this is typically obtained from the `CardEmbedMetadata`.
- If you have forced OAuth enabled, you also need to pass through the `embedAuthUrl` from `CardEmbedMetadata`.

```js

// ... rest of your component

<ExperienceModal
  isOpen={isModalOpen}
  onClose={() => setIsModalOpen(false)}
  embedUrl={metadata?.embedUrl}
  embedAuthUrl={metadata?.embedAuthUrl}
  loadingTimeoutMs={15000} // Optional: customize loading timeout
  appId="my-app" // Optional: identify where the content is embedded
/>;
```

### Experience Modal example without Card Cover

- You can use whichever components you want to build your desired experience.
- For example, you can trigger the `ExperienceModal` without rendering our CardCover.

```js
// ... rest of your component
const { data, isLoading } = useFetchCardMetadata({
  isEnabled,
  embedMetaUrl: EmbedMetaUrlFromKomoPortal
});
const [modalOpen, setModalOpen] = useState(false);

// other code, e.g. some element that calls setModalOpen(true) after isLoading returns false

<ExperienceModal
  isOpen={modalOpen}
  onClose={() => {
    setModalOpen(false);
  }}
  embedUrl={data?.embedUrl}
/>;
```

### Listening for events from the embedded experience

- You can listen for events from the embedded experience by using the `onWindowMessage` or `onKomoEvent` props on the `ExperienceModal` or `KomoCardWidget`.
- The `onWindowMessage` prop exposes any `window.postMessage` events from the embedded experience.
- The `onKomoEvent` prop exposes any [User Interaction Events](/user-interaction-events) from the embedded experience.
  - Note: User Interaction Events will also appear in the `onWindowMessage` callback. `onKomoEvent` is a more convenient way to listen for Komo User Interaction Events from the embedded experience.

```js
<ExperienceModal
  isOpen={modalOpen}
  onClose={() => {
    setModalOpen(false);
  }}
  embedUrl={data?.embedUrl}
  onKomoEvent={(event) => {
    console.log('Komo event received:', event);
  }}
  onWindowMessage={(event) => {
    console.log('Window message received:', event);
  }}
/>
```

### Extension Data

- The `ExperienceModal` and `KomoCardWidget` components allow you to set [extension data](/user-interaction-events/extension-data) on the user interaction events.
- The `extensionDataValues` prop is a plain `Record<string, string | number | boolean | object>` object.
- Make sure PII is not passed in as extension data, as it is passed directly to your tag manager integrations.

```js
<KomoCardWidget
  embedMetaUrl={KomoCardNativeEmbedUrl}
  extensionDataValues={{
    custom_unique_id: 'ABC123',
    custom_object: {
      some_id: 'ABC123',
      some_measure: 123456
    }
  }}
/>
```

### Query Parameters

Pass custom query parameters (like UTM tracking parameters) to your embedded experiences. This is useful for campaign attribution, A/B testing, and analytics tracking.

#### Basic Usage

```js

<KomoCardWidget
  embedMetaUrl={KomoCardNativeEmbedUrl}
  queryParams={{
    utm_source: 'mobile-app',
    utm_medium: 'widget',
    utm_campaign: 'summer-2024',
    utm_content: 'hero-banner'
  }}
/>;
```

#### UTM Tracking Example

```js
<KomoCardWidget
  embedMetaUrl={embedUrl}
  queryParams={{
    utm_source: 'instagram',
    utm_medium: 'social',
    utm_campaign: 'product-launch',
    utm_term: 'keyword',
    utm_content: 'story-swipe-up'
  }}
/>
```

#### Dynamic Campaign Tracking

```js

function CampaignScreen() {
  const route = useRoute();
  const { campaignId, source } = route.params;

  return (
    <KomoCardWidget
      embedMetaUrl={embedUrl}
      queryParams={{
        utm_campaign: campaignId,
        utm_source: source,
        utm_medium: 'app'
      }}
    />
  );
}
```

#### User Segment Tracking

```js

function ContestScreen() {
  const { userSegment, subscriptionTier } = useUser();

  return (
    <KomoCardWidget
      embedMetaUrl={embedUrl}
      queryParams={{
        utm_source: 'app',
        utm_content: userSegment,
        user_tier: subscriptionTier
      }}
    />
  );
}
```

#### Combining with Other Props

Query parameters work alongside form prefill and extension data:

```js
<KomoCardWidget
  embedMetaUrl={embedUrl}
  // Prefill form fields
  formPrefillValues={{
    email: user.email,
    first_name: user.firstName
  }}
  // Add analytics data
  extensionDataValues={{
    user_id: user.id,
    account_type: user.accountType
  }}
  // Track campaign source
  queryParams={{
    utm_source: 'app',
    utm_medium: 'home-screen',
    utm_campaign: 'welcome-flow'
  }}
/>
```

#### Using with ExperienceModal

The `ExperienceModal` component also supports `queryParams`:

```js

function CustomModal() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <Button title="Open Contest" onPress={() => setIsOpen(true)} />

      <ExperienceModal
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        embedUrl={metadata?.embedUrl}
        queryParams={{
          utm_source: 'custom-modal',
          utm_medium: 'app'
        }}
      />
    </>
  );
}
```

#### Deep Link Attribution

Track users coming from deep links:

```js

function DeepLinkScreen() {
  const [queryParams, setQueryParams] = useState({});

  useEffect(() => {
    // Parse deep link URL parameters
    Linking.getInitialURL().then((url) => {
      if (url) {
        const urlObj = new URL(url);
        const params = {};
        urlObj.searchParams.forEach((value, key) => {
          params[key] = value;
        });
        setQueryParams(params);
      }
    });
  }, []);

  return (
    <KomoCardWidget
      embedMetaUrl={embedUrl}
      queryParams={{
        ...queryParams,
        utm_source: 'deep-link'
      }}
    />
  );
}
```

#### Multi-Platform Tracking

Detect and track the platform:

```js

<KomoCardWidget
  embedMetaUrl={embedUrl}
  queryParams={{
    utm_source: 'react-native',
    utm_medium: 'app',
    platform: Platform.OS, // 'ios' or 'android'
    app_version: DeviceInfo.getVersion()
  }}
/>;
```

#### Technical Details

- Query parameters are added directly to the webview URL (no prefix)
- All values must be strings
- Parameters are available to analytics tools in the embedded experience
- Can be combined with `formPrefillValues` and `extensionDataValues`
- Supports standard UTM parameters and custom parameters

### Auth0 Session Transfer

- The `KomoCardWidget` component supports Auth0 authentication through the `authPassthroughParams` prop.
- The `ExperienceModal` component supports Auth0 authentication through the `embedAuthUrl` and `authPassthroughParams` props.
  - The `embedAuthUrl` is typically obtained from the `CardEmbedMetadata`.
- Pre-requisites:
  - Auth0 SSO must be configured on the Komo Hub, and "Force Embed Auth" must be enabled under Embed SDK settings on the hub.
  - Pass a fresh session transfer token to the `authPassthroughParams` prop, e.g. `session_transfer_token: 'ABC123'`.
- With this setup, the user will be redirected to Auth0 to authenticate when the experience modal is opened, before being redirected back to the embedded experience.
- The session transfer token must be obtained immediately before opening the experience modal, since it has a short 60 second lifespan.
- **Recommended:** Specify `webViewProps={{ incognito: true }}` to ensure user session state is cleared correctly when changing between different authenticated users.

```js
<KomoCardWidget
    embedMetaUrl={KomoCardNativeEmbedUrl}
    authPassthroughParams={new URLSearchParams({
        session_transfer_token: 'ABC123'
    })}
    webViewProps={{ incognito: true }}
/>

// or if using the ExperienceModal directly
<ExperienceModal
    isOpen={isModalOpen}
    onClose={() => setIsModalOpen(false)}
    embedUrl={metadata?.embedUrl}
    embedAuthUrl={metadata?.embedAuthUrl}
    authPassthroughParams={new URLSearchParams({
        session_transfer_token: 'ABC123'
    })}
    webViewProps={{ incognito: true }}
/>
```

#### Error handling

- If the `session_transfer_token` passed to Auth0 is used, invalid, or expired, then the users will end up being shown the ExperienceModal `errorDisplay`, which includes a built-in retry button.
- If you don't provide an `errorDisplay` override, the retry function will just attempt to reload the experience with the current parameters and will most likely fail again.
- We recommend that you provide a custom `errorDisplay` so that you can handle `session_transfer_token` regeneration before trying to load the content again.

## API Reference

### ButtonStyle

| Name            | Type    | Description                    | Required |
| --------------- | ------- | ------------------------------ | -------- |
| text            | string? | Text to display on the button  | Optional |
| backgroundColor | string? | Background color of the button | Optional |
| color           | string? | Text color of the button       | Optional |

### CardCover Props

| Name                    | Type                     | Description                                                | Required |
| ----------------------- | ------------------------ | ---------------------------------------------------------- | -------- |
| onClick                 | () => void               | The callback for when the cover is clicked                 | Required |
| isLoading               | boolean                  | Whether the cover is loading                               | Required |
| isError                 | boolean?                 | Whether the cover is in an error state                     | Optional |
| loader                  | ReactNode?               | Override the default skeleton loader                       | Optional |
| errorDisplay            | ReactNode?               | Override the default error display                         | Optional |
| metaButtonStyle         | ButtonStyle?             | The button style returned from the embed metadata endpoint | Optional |
| overrideButtonStyle     | `StyleProp<ViewStyle>`?  | Override the button style                                  | Optional |
| overrideButtonTextStyle | `StyleProp<TextStyle>`?  | Override the button text style                             | Optional |
| containerStyle          | `StyleProp<ViewStyle>`?  | Override the container style                               | Optional |
| coverImageStyle         | `StyleProp<ImageStyle>`? | Override the cover image style                             | Optional |
| hideCoverButton         | boolean?                 | Whether to hide the cover button                           | Optional |
| imageUrl                | string?                  | The url of the cover image                                 | Optional |
| imageAspectRatio        | number?                  | The aspect ratio of the cover image                        | Optional |

### CardEmbedMetadata

| Name             | Type         | Description                                                   | Required |
| ---------------- | ------------ | ------------------------------------------------------------- | -------- |
| title            | string?      | The title of the card                                         | Optional |
| imageUrl         | string?      | URL of the card's cover image                                 | Optional |
| imageHeight      | number?      | Height of the cover image in pixels                           | Optional |
| imageWidth       | number?      | Width of the cover image in pixels                            | Optional |
| imageAspectRatio | number?      | Aspect ratio of the cover image                               | Optional |
| embedUrl         | string?      | URL for the embedded experience                               | Optional |
| embedAuthUrl     | string?      | URL used to OAuth user before showing the embedded experience | Optional |
| buttonStyle      | ButtonStyle? | Styling for the card's button                                 | Optional |

### ExperienceModal Props

| Name                  | Type                                                    | Description                                                                    | Required |
| --------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------ | -------- |
| isOpen                | boolean                                                 | Whether the modal is open                                                      | Required |
| onClose               | () => void                                              | Callback for when close is requested                                           | Required |
| embedUrl              | string                                                  | The URL of the embedded card experience                                        | Required |
| modalHeader           | ReactNode                                               | Override the default modal header                                              | Optional |
| shareClickUrl         | string                                                  | Override the url that redirects a user when clicking on a share link           | Optional |
| appId                 | string                                                  | An identifier for the embedded Komo content                                    | Optional |
| formPrefillValues     | `Record<string, string>`                                | Prefill values for the form within the experience                              | Optional |
| extensionDataValues   | `Record<string, string \| number \| boolean \| object>` | Extension data values for the experience                                       | Optional |
| loadingIndicator      | ReactNode                                               | Override the default loading indicator                                         | Optional |
| modalProps            | `ModalProps`                                            | Override the default modal props                                               | Optional |
| loadingTimeoutMs      | number                                                  | Timeout in milliseconds before showing error state. Defaults to 15000ms        | Optional |
| errorDisplay          | `({ onRetry }: { onRetry: () => void }) => ReactNode`   | Override the default error display                                             | Optional |
| onFileDownload        | `WebViewProps["onFileDownload"]`                        | Callback for when a file download is requested. Only applies to iOS            | Optional |
| onKomoEvent           | `(event: KomoEvent) => void`                            | Callback for when a Komo event is raised in the embedded experience            | Optional |
| onWindowMessage       | `(event: any) => void`                                  | Callback for when a window message is raised in the embedded experience        | Optional |
| embedAuthUrl          | string?                                                 | The URL of the authorization endpoint                                          | Optional |
| authPassthroughParams | URLSearchParams                                         | Passthrough parameters to add to the auth URL as query parameters              | Optional |
| webViewProps          | `WebViewProps`                                          | Additional props for the react-native-webview component                        | Optional |
| iframeProps           | `IframeProps`                                           | Additional props for the iframe component. Only applies if the platform is web | Optional |

### KomoCardWidget Props

| Name                  | Type                                                    | Description                                                                          | Required |
| --------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------ | -------- |
| embedMetaUrl          | String                                                  | The native embed url taken from the Komo portal settings of the card to be embedded  | Required |
| appId                 | String                                                  | Useful for the embedded Komo content to identify where it's being embedded           | Optional |
| containerStyle        | `StyleProp<ViewStyle>`                                  | Style overrides for the container of the card cover                                  | Optional |
| coverImageStyle       | `StyleProp<ImageStyle>`                                 | Style overrides for the image of the card cover                                      | Optional |
| buttonStyle           | `StyleProp<ViewStyle>`                                  | Style overrides for the CTA button of the card cover                                 | Optional |
| buttonTextStyle       | `StyleProp<TextStyle>`                                  | Style overrides for the CTA button text of the card cover                            | Optional |
| coverLoader           | ReactNode                                               | The loader shown while the cover is loading                                          | Optional |
| coverErrorDisplay     | ReactNode                                               | Override the default error display for the cover                                     | Optional |
| hideCoverButton       | Boolean                                                 | Hide the CTA button of the cover                                                     | Optional |
| modalHeader           | ReactNode                                               | The header of the modal to render instead of the default                             | Optional |
| onError               | `(error) => void`                                       | Callback for when an error occurs during querying the embed metadata endpoint        | Optional |
| onModalClose          | `() => void`                                            | Callback on modal close                                                              | Optional |
| onModalOpen           | `() => void`                                            | Callback on modal open                                                               | Optional |
| shareClickUrl         | String                                                  | Override of the url that redirects a user when clicking on a share link              | Optional |
| formPrefillValues     | `Record<string, string>`                                | Prefill values for the form within the experience                                    | Optional |
| extensionDataValues   | `Record<string, string \| number \| boolean \| object>` | Extension data values for the experience                                             | Optional |
| onFileDownload        | `WebViewProps["onFileDownload"]`                        | Callback for when a file download is requested. Only applies to iOS                  | Optional |
| onKomoEvent           | `(event: KomoEvent) => void`                            | Callback for when a Komo event is raised in the embedded experience                  | Optional |
| onWindowMessage       | `(event: any) => void`                                  | Callback for when a window message is raised in the embedded experience              | Optional |
| authPassthroughParams | URLSearchParams                                         | Passthrough parameters to add to the auth URL as query parameters                    | Optional |
| loadingTimeoutMs      | number                                                  | Timeout in milliseconds before showing error state in the modal. Defaults to 15000ms | Optional |
| modalErrorDisplay     | `({ onRetry }: { onRetry: () => void }) => ReactNode`   | Override the default error display for the modal                                     | Optional |
| webViewProps          | `WebViewProps`                                          | Additional props for the react-native-webview component                              | Optional |
| iframeProps           | `IframeProps`                                           | Additional props for the iframe component. Only applies if the platform is web       | Optional |

### KomoEvent

| Name          | Type                                                    | Description                                | Required |
| ------------- | ------------------------------------------------------- | ------------------------------------------ | -------- |
| eventName     | string                                                  | The name of the event                      | Required |
| eventData     | any                                                     | Object containing the event data           | Required |
| extensionData | `Record<string, string \| number \| boolean \| object>` | Extension data raised along with the event | Required |

### useFetchCardMetadata Hook

#### Options

| Name         | Type             | Description                                                                   | Required |
| ------------ | ---------------- | ----------------------------------------------------------------------------- | -------- |
| embedMetaUrl | string           | The URL of the embed metadata for the card, copied from the Komo Portal       | Required |
| isEnabled    | boolean          | Whether the embed metadata query is enabled. Defaults to true                 | Optional |
| onError      | (e: any) => void | Callback for when an error occurs during querying the embed metadata endpoint | Optional |

#### Result

| Name         | Type                  | Description                                | Required |
| ------------ | --------------------- | ------------------------------------------ | -------- |
| data         | `CardEmbedMetadata?`  | The embed metadata for the card            | Optional |
| isLoading    | boolean               | Whether the embed metadata is loading      | Required |
| isError      | boolean               | Whether the embed metadata query failed    | Required |
| isSuccess    | boolean               | Whether the embed metadata query succeeded | Required |
| refetchAsync | `() => Promise<void>` | Function to refetch the embed metadata     | Required |
