Komo cards can be embedded into your React Native application through Komo's package @komo-tech/react-native.

Installation

pnpm add @komo-tech/react-native react-native-webview react-native-safe-area-context
npm install @komo-tech/react-native react-native-webview react-native-safe-area-context
yarn add @komo-tech/react-native react-native-webview react-native-safe-area-context

Basic usage

  • Wrap embedded content in KomoClientProvider.
  • Use KomoCard inside the provider for the fastest setup. It combines metadata fetching, card cover display, and modal handling.
  • Preferred: pass cardId under KomoClientProvider. The provider's App ID region prefix selects the regional metadata host. To find the card ID in Komo Portal:
    • Navigate to the settings of the card to be embedded.
    • Select the Embed tab and click React Native code in the right sidebar.
    • Copy the generated cardId from the card embed settings or the card actions.
  • Deprecated: embedMetaUrl still works inside or outside the provider for legacy, providerless, custom-host, and emulator flows. When both sources are supplied, cardId takes precedence.
import { KomoCard, KomoClientProvider } from '@komo-tech/react-native';

<KomoClientProvider
  appId="au1_00000000-0000-0000-0000-000000000000"
  getIdentityToken={async () => {
    const token = await getHostJwt();
    return token ? { type: 'jwt', token } : { type: 'anonymous' };
  }}
>
  <KomoCard
    cardId="00000000-0000-0000-0000-000000000000"
    containerStyle={{ maxWidth: '80%' }}
  />
</KomoClientProvider>;

Provider authentication

Use KomoClientProvider for all supported new React Native integrations. It creates the Komo session shared by KomoCard and direct KomoExperienceModal usage beneath it.

appId, getIdentityToken, and children are required.

import {
  KomoCard,
  KomoClientProvider,
  isKomoAuthError,
  useKomoSession
} from '@komo-tech/react-native';

<KomoClientProvider
  appId="au1_00000000-0000-0000-0000-000000000000"
  getIdentityToken={async () => {
    const token = await getHostJwt();
    return token ? { type: 'jwt', token } : { type: 'anonymous' };
  }}
  onAuthenticated={({ contactId, trustLevel }) => {
    trackKomoAuth(contactId, trustLevel);
  }}
  onIdentityChanged={({ previousContactId, contactId, reason }) => {
    trackKomoIdentityChange(previousContactId, contactId, reason);
  }}
  onSessionExpired={({ reason }) => {
    promptUserToSignInAgain(reason);
  }}
  onAuthError={(error) => {
    reportAuthError(error.code, error.retryable);
  }}
>
  <KomoCard cardId="00000000-0000-0000-0000-000000000000" />
</KomoClientProvider>;

const retryIdentify = async (
  komoSession: ReturnType<typeof useKomoSession>
) => {
  try {
    await komoSession.identify();
  } catch (error) {
    if (isKomoAuthError(error) && error.retryable) {
      showRetryPrompt();
    }
  }
};

App ID and identity results

Copy the SDK App ID from Komo Portal. It includes the region prefix in {region}_{appId} format, for example au1_00000000-0000-0000-0000-000000000000. See Workspace Apps for setup steps and identity result behavior. See When getIdentityToken runs for call frequency and refresh behavior.

Return one of these identity results from getIdentityToken:

  • { type: 'jwt', token } exchanges your host JWT for a Komo session.
  • { type: 'email', attributes: { email, ... } } exchanges verified host attributes, attributes.email is required. By default, attributes map to matching contact properties by key. Configure custom mappings in the Workspace App when host attribute keys do not match Komo contact property keys.
  • { type: 'anonymous' } creates an anonymous Komo session. Use this when no host JWT exists but embeds should work for visitors.
  • { type: 'none' } creates no Komo session. Session-backed SDK features will not work.
<KomoClientProvider
  appId="au1_00000000-0000-0000-0000-000000000000"
  getIdentityToken={async () => {
    const token = await getHostJwt();
    return token ? { type: 'jwt', token } : { type: 'anonymous' };
  }}
>
  <KomoCard cardId="00000000-0000-0000-0000-000000000000" />
</KomoClientProvider>

Provider callbacks

Use provider callbacks for session lifecycle UI and analytics:

  • onAuthenticated runs when Komo resolves a session.
  • onIdentityChanged runs when the provider resolves a different identity.
  • onSessionExpired runs when a session expires, refresh fails, or the user logs out.
  • onAuthError receives a KomoAuthError for authentication failures.
<KomoClientProvider
  appId="au1_00000000-0000-0000-0000-000000000000"
  getIdentityToken={getKomoIdentity}
  onAuthError={(error) => {
    switch (error.code) {
      case 'identity_provider_failed':
        showAuthServiceUnavailableMessage();
        break;
      case 'token_invalid':
        promptUserToSignInAgain();
        break;
      default:
        logAuthError(error.code, error.message, error.retryable);
        break;
    }
  }}
>
  <KomoCard cardId="00000000-0000-0000-0000-000000000000" />
</KomoClientProvider>

useKomoSession

Use useKomoSession under KomoClientProvider when app UI needs session state or explicit auth actions.

import { useKomoSession } from '@komo-tech/react-native';
import { Button } from 'react-native';

function SessionActions() {
  const {
    session,
    getSessionToken,
    getCurrentSession,
    identify,
    logout,
    appId,
    sdkInstanceId
  } = useKomoSession();

  return (
    <Button
      title={`Refresh Komo session for ${appId}`}
      onPress={async () => {
        await identify();
        await getSessionToken();
        console.log(getCurrentSession(), session.state, sdkInstanceId);
      }}
    />
  );
}

Prefilling form details

  • Pass information through to Komo forms with formPrefillValues.
  • Use a plain Record<string, string> object.
  • Object keys must match the Unique ID of the Komo form field or contact property.
<KomoCard
  cardId="00000000-0000-0000-0000-000000000000"
  containerStyle={{ maxWidth: '80%' }}
  formPrefillValues={{
    email: 'email@domain.com',
    first_name: 'Person',
    last_name: 'Doe'
  }}
/>

Advanced usage

Metadata fetching

Preferred: use useCardMetadataQuery with cardId under KomoClientProvider. The provider resolves the regional metadata URL from its App ID prefix.

Using cardId outside KomoClientProvider throws:

useCardMetadataQuery cardId must be used within a KomoClientProvider

Deprecated: pass embedMetaUrl for providerless, custom-host, or emulator-specific metadata URLs. When both sources are supplied, cardId takes precedence.

import { useCardMetadataQuery } from '@komo-tech/react-native';

// Preferred: under KomoClientProvider
const { data, isLoading, isError } = useCardMetadataQuery({
  cardId: '00000000-0000-0000-0000-000000000000'
});

// Deprecated compatibility (works without a provider):
// useCardMetadataQuery({ embedMetaUrl: KomoCardNativeEmbedUrl });

For custom fetching, call useKomoSession().buildCardEmbedMetaUrl(cardId) under the provider to derive the regional metadata URL, then fetch with your own data layer.

import { useKomoSession } from '@komo-tech/react-native';

const { buildCardEmbedMetaUrl } = useKomoSession();
const metadataUrl = buildCardEmbedMetaUrl(
  '00000000-0000-0000-0000-000000000000'
);
// e.g. https://api.au1.komo.site/api/v1/live/cards/.../embed-meta

buildCardEmbedMetaUrl validates the card ID, encodes it as one path segment, and uses the provider's resolved region.

CardEmbedMetadata has the information required to render the cover image and the URL that KomoExperienceModal needs to render the embedded experience. You can use your own data-fetching layer if it produces compatible metadata.

Render a card cover

Use KomoCardCover to display the cover image of a Komo card.

import { KomoCardCover } from '@komo-tech/react-native';

<KomoCardCover
  imageUrl={metadata?.imageUrl}
  imageAspectRatio={metadata?.imageAspectRatio}
  isLoading={isLoading}
  isError={isError}
  onClick={() => setIsModalOpen(true)}
  metaButtonStyle={metadata?.buttonStyle}
  containerStyle={{ borderRadius: 8 }}
/>;

Use the experience modal directly

Use KomoExperienceModal when you want to control the trigger UI yourself. Render it under KomoClientProvider so provider session-token auth is applied.

import { KomoExperienceModal } from '@komo-tech/react-native';

<KomoExperienceModal
  isOpen={isModalOpen}
  onClose={() => setIsModalOpen(false)}
  embedUrl={metadata?.embedUrl}
  loadingTimeoutMs={15000}
/>;

Example without rendering a Komo cover:

const { data, isLoading } = useCardMetadataQuery({
  isEnabled,
  cardId: '00000000-0000-0000-0000-000000000000'
});
const [modalOpen, setModalOpen] = useState(false);

return (
  <>
    <Button
      title="Open Komo experience"
      disabled={isLoading}
      onPress={() => setModalOpen(true)}
    />
    <KomoExperienceModal
      isOpen={modalOpen}
      onClose={() => setModalOpen(false)}
      embedUrl={data?.embedUrl}
    />
  </>
);

Listening for events

  • Use onKomoEvent for User Interaction Events.
  • Use onWindowMessage for raw window.postMessage events from the embedded experience.
  • User Interaction Events also appear in onWindowMessage. onKomoEvent is usually more convenient.
<KomoExperienceModal
  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

KomoExperienceModal and KomoCard can set extension data on User Interaction Events. Avoid PII because extension data is passed to tag manager integrations.

<KomoCard
  cardId="00000000-0000-0000-0000-000000000000"
  extensionDataValues={{
    custom_unique_id: 'ABC123',
    custom_object: {
      some_id: 'ABC123',
      some_measure: 123456
    }
  }}
/>

Query parameters

Pass custom query parameters, such as UTM values, to embedded experiences with queryParams.

import { KomoCard } from '@komo-tech/react-native';

<KomoCard
  cardId="00000000-0000-0000-0000-000000000000"
  queryParams={{
    utm_source: 'mobile-app',
    utm_medium: 'widget',
    utm_campaign: 'summer-2024',
    utm_content: 'hero-banner'
  }}
/>;

These examples assume KomoCard and KomoExperienceModal render under KomoClientProvider.

UTM tracking example

<KomoCard
  cardId={cardId}
  queryParams={{
    utm_source: 'instagram',
    utm_medium: 'social',
    utm_campaign: 'product-launch',
    utm_term: 'keyword',
    utm_content: 'story-swipe-up'
  }}
/>

Dynamic campaign tracking

import { KomoCard } from '@komo-tech/react-native';
import { useRoute } from '@react-navigation/native';

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

  return (
    <KomoCard
      cardId={cardId}
      queryParams={{
        utm_campaign: campaignId,
        utm_source: source,
        utm_medium: 'app'
      }}
    />
  );
}

User segment tracking

import { KomoCard } from '@komo-tech/react-native';
import { useUser } from './hooks/useUser';

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

  return (
    <KomoCard
      cardId={cardId}
      queryParams={{
        utm_source: 'app',
        utm_content: userSegment,
        user_tier: subscriptionTier
      }}
    />
  );
}

Combining with other props

Query parameters work alongside form prefill and extension data:

<KomoCard
  cardId={cardId}
  formPrefillValues={{
    email: user.email,
    first_name: user.firstName
  }}
  extensionDataValues={{
    user_id: user.id,
    account_type: user.accountType
  }}
  queryParams={{
    utm_source: 'app',
    utm_medium: 'home-screen',
    utm_campaign: 'welcome-flow'
  }}
/>

queryParams also works with KomoExperienceModal:

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

Track users coming from deep links:

import { KomoCard } from '@komo-tech/react-native';
import { useEffect, useState } from 'react';
import { Linking } from 'react-native';

function DeepLinkScreen() {
  const [queryParams, setQueryParams] = useState<Record<string, string>>({});

  useEffect(() => {
    Linking.getInitialURL().then((url) => {
      if (!url) return;

      const urlObj = new URL(url);
      const params: Record<string, string> = {};
      urlObj.searchParams.forEach((value, key) => {
        params[key] = value;
      });
      setQueryParams(params);
    });
  }, []);

  return (
    <KomoCard
      cardId={cardId}
      queryParams={{
        ...queryParams,
        utm_source: 'deep-link'
      }}
    />
  );
}

Multi-platform tracking

Detect and track the platform:

import { KomoCard } from '@komo-tech/react-native';
import { Platform } from 'react-native';

<KomoCard
  cardId={cardId}
  queryParams={{
    utm_source: 'react-native',
    utm_medium: 'app',
    platform: Platform.OS,
    app_version: getAppVersion()
  }}
/>;

Query parameters are added directly to the webview URL. All values must be strings. They can be combined with formPrefillValues and extensionDataValues.

Legacy Auth0 session transfer

This section applies only to older integrations that do not use KomoClientProvider. New integrations should use provider authentication.

  • Legacy integrations can pass Auth0 authentication through authPassthroughParams on KomoCard.
  • Legacy direct KomoExperienceModal usage can pass Auth0 authentication through embedAuthUrl and authPassthroughParams.
  • embedAuthUrl is typically obtained from CardEmbedMetadata.
  • Auth0 SSO must be configured on the Komo site, and Force Embed Auth must be enabled under Embed SDK settings.
  • The session transfer token must be obtained immediately before opening the modal because it has a short lifespan.
  • Specify webViewProps={{ incognito: true }} to clear session state when changing between authenticated users.
{/* Legacy providerless Auth0 transfer (deprecated embedMetaUrl path) */}
<KomoCard
  embedMetaUrl={KomoCardNativeEmbedUrl}
  authPassthroughParams={new URLSearchParams({
    session_transfer_token: 'ABC123'
  })}
  webViewProps={{ incognito: true }}
/>

<KomoExperienceModal
  isOpen={isModalOpen}
  onClose={() => setIsModalOpen(false)}
  embedUrl={metadata?.embedUrl}
  embedAuthUrl={metadata?.embedAuthUrl}
  authPassthroughParams={new URLSearchParams({
    session_transfer_token: 'ABC123'
  })}
  webViewProps={{ incognito: true }}
/>

Auth0 error handling

If the session_transfer_token is used, invalid, or expired, the modal shows its errorDisplay, including a built-in retry button. Provide a custom errorDisplay if you need to regenerate the token before retrying.

API reference

KomoClientProvider props

| Name | Type | Required | Description | | ------------------- | ------------------------------------------------- | -------- | ----------------------------------------------------------------- | | appId | string | Yes | Workspace App ID copied from Komo Portal, including region prefix | | getIdentityToken | () => Promise<IdentityResult> \| IdentityResult | Yes | Returns identity used for session exchange | | children | ReactNode | Yes | Components that should share the provider session | | fetch | PortableFetch \| RuntimeFetch | No | Custom fetch implementation. Defaults to globalThis.fetch | | logger | EmbedCoreLogger | No | Optional logger passed to the session manager | | onAuthenticated | ({ contactId, trustLevel }) => void | No | Called when provider session authenticates | | onIdentityChanged | (payload: IdentityChangedPayload) => void | No | Called when provider session resolves to a different identity | | onSessionExpired | ({ reason }) => void | No | Called when provider session expires, refresh fails, or logs out | | onAuthError | (error: KomoAuthError) => void | No | Called when provider authentication or refresh fails |

useKomoSession return

| Name | Type | Description | | ----------------------- | ------------------------------- | ---------------------------------------------------------- | | session | CurrentSession | Latest provider session snapshot | | getSessionToken | () => Promise<string \| null> | Returns the current bearer token and refreshes state | | getCurrentSession | () => CurrentSession | Imperative current session snapshot read | | identify | () => Promise<CurrentSession> | Forces identity exchange using latest identity result | | logout | () => void | Logs out the current Komo session | | buildCardEmbedMetaUrl | (cardId: string) => string | Derives the regional card metadata URL for custom fetching | | appId | string | Resolved app ID segment used by the provider | | sdkInstanceId | string | Stable SDK instance ID used by the provider |

KomoAuthError

KomoAuthError is the stable error contract for provider authentication failures.

| Name | Type | Description | | --------------- | -------------------------- | ------------------------------------------ | | name | 'KomoAuthError' | Error type discriminator | | code | KomoAuthErrorCode | Stable machine-readable reason | | source | 'app' \| 'komo' \| 'sdk' | Origin of the failure | | retryable | boolean | Whether retrying may succeed | | status | number? | HTTP status when Komo returned one | | failureReason | ExchangeFailureReason? | Raw Komo auth failure reason when returned | | message | string | Human-readable diagnostic message |

CardEmbedMetadata

| Name | Type | Description | | ------------------ | -------------- | ------------------------------------------------------- | | title | string? | Title of the card | | imageUrl | string? | URL of the card cover image | | imageHeight | number? | Height of the cover image in pixels | | imageWidth | number? | Width of the cover image in pixels | | imageAspectRatio | number? | Aspect ratio of the cover image | | embedUrl | string? | URL for the embedded experience | | embedAuthUrl | string? | Legacy OAuth URL before showing the embedded experience | | buttonStyle | ButtonStyle? | Styling for the card button |

KomoCardCover props

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

KomoExperienceModal props

| Name | Type | Required | Description | | ----------------------- | ------------------------------------------------------- | -------- | ------------------------------------------------------- | | isOpen | boolean | Yes | Whether the modal is open | | onClose | () => void | Yes | Callback for when close is requested | | embedUrl | string | Yes | URL of the embedded card experience | | modalHeader | ReactNode | No | Override the default modal header | | shareClickUrl | string | No | Override the share link redirect URL | | appId | string | No | Identifier for where the content is embedded | | formPrefillValues | Record<string, string> | No | Prefill values for the experience | | extensionDataValues | Record<string, string \| number \| boolean \| object> | No | Extension data included with User Interaction Events | | queryParams | Record<string, string> | No | Query parameters to add to the webview URL | | loadingIndicator | ReactNode | No | Override the default loading indicator | | modalProps | ModalProps | No | Override the default modal props | | safeAreaScrimColor | ColorValue | No | Color used to fill safe area above modal content | | loadingTimeoutMs | number | No | Timeout before showing error state. Defaults to 15000ms | | errorDisplay | ({ onRetry }: { onRetry: () => void }) => ReactNode | No | Override the default error display | | onFileDownload | WebViewProps['onFileDownload'] | No | Callback for file downloads. Only applies to iOS | | onKomoEvent | (event: KomoEvent) => void | No | Callback for Komo events | | onWindowMessage | (event: any) => void | No | Callback for raw window messages | | embedAuthUrl | string | No | Legacy authorization URL | | authPassthroughParams | URLSearchParams | No | Legacy authorization passthrough parameters | | webViewProps | WebViewProps | No | Props for react-native-webview | | iframeProps | IframeProps | No | Props for the web iframe | | debugMode | boolean | No | Enables development troubleshooting logs |

KomoCard props

| Name | Type | Required | Description | | ----------------------- | ------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------ | | cardId | string | No* | Preferred. Card ID resolved through the provider's regional metadata host | | embedMetaUrl | string | No* | Deprecated. Explicit metadata URL for legacy, providerless, or custom-host flows | | appId | string | No | Identifier for where the content is embedded | | containerStyle | StyleProp<ViewStyle> | No | Override the container style | | coverImageStyle | StyleProp<ImageStyle> | No | Override the cover image style | | buttonStyle | StyleProp<ViewStyle> | No | Override the button style | | buttonTextStyle | StyleProp<TextStyle> | No | Override the button text style | | coverLoader | ReactNode | No | Override the default loader for the cover | | coverErrorDisplay | ReactNode | No | Override the default error display for the cover | | hideCoverButton | boolean | No | Whether to hide the cover button | | modalHeader | ReactNode | No | Override the default modal header | | onError | (e: any) => void | No | Callback for metadata query errors | | onModalClose | () => void | No | Callback when the modal closes | | onModalOpen | () => void | No | Callback when the modal opens | | shareClickUrl | string | No | Override the share link redirect URL | | formPrefillValues | Record<string, string> | No | Prefill values for the experience | | extensionDataValues | Record<string, string \| number \| boolean \| object> | No | Extension data included with User Interaction Events | | queryParams | Record<string, string> | No | Query parameters to add to the webview URL | | authPassthroughParams | URLSearchParams | No | Legacy authorization passthrough parameters | | loadingTimeoutMs | number | No | Timeout before showing modal error state | | modalErrorDisplay | ({ onRetry }: { onRetry: () => void }) => ReactNode | No | Override modal error display | | onFileDownload | WebViewProps['onFileDownload'] | No | Callback for file downloads. Only applies to iOS | | onKomoEvent | (event: KomoEvent) => void | No | Callback for Komo events | | onWindowMessage | (event: any) => void | No | Callback for raw window messages | | webViewProps | WebViewProps | No | Props for react-native-webview | | iframeProps | IframeProps | No | Props for the web iframe | | debugMode | boolean | No | Enables development troubleshooting logs |

*At least one of cardId or embedMetaUrl is required. When both are supplied, cardId takes precedence.

useCardMetadataQuery hook

Provide cardId (preferred, requires KomoClientProvider) or deprecated embedMetaUrl. At least one source is required; cardId wins when both are present.

Options

| Name | Type | Required | Description | | -------------- | ------------------ | -------- | ------------------------------------------------------------------------- | | cardId | string | No* | Preferred. Card ID resolved through the provider's regional metadata host | | embedMetaUrl | string | No* | Deprecated. Explicit metadata URL for legacy or providerless flows | | isEnabled | boolean | No | Whether the metadata query is enabled. Defaults to true | | onError | (e: any) => void | No | Callback for metadata query errors |

*At least one of cardId or embedMetaUrl is required.

Result

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

KomoEvent

| Name | Type | Description | | --------------- | ------------------------------------------------------- | -------------------------------------------------------------------- | | eventName | string | Name of the Komo event | | eventData | any | Object containing event data | | extensionData | Record<string, string \| number \| boolean \| object> | Extension data raised along with the event | | trackedData | Record<string, string \| number \| boolean \| object> | Optional. Admin-configured contact properties included on the event |