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
KomoCardinside the provider for the fastest setup. It combines metadata fetching, card cover display, and modal handling. - Preferred: pass
cardIdunderKomoClientProvider. 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
Embedtab and clickReact Native codein the right sidebar. - Copy the generated
cardIdfrom the card embed settings or the card actions.
- Deprecated:
embedMetaUrlstill works inside or outside the provider for legacy, providerless, custom-host, and emulator flows. When both sources are supplied,cardIdtakes 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.emailis 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:
onAuthenticatedruns when Komo resolves a session.onIdentityChangedruns when the provider resolves a different identity.onSessionExpiredruns when a session expires, refresh fails, or the user logs out.onAuthErrorreceives aKomoAuthErrorfor 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
onKomoEventfor User Interaction Events. - Use
onWindowMessagefor rawwindow.postMessageevents from the embedded experience. - User Interaction Events also appear in
onWindowMessage.onKomoEventis 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'
}}
/>
Deep link attribution
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
authPassthroughParamsonKomoCard. - Legacy direct
KomoExperienceModalusage can pass Auth0 authentication throughembedAuthUrlandauthPassthroughParams. embedAuthUrlis typically obtained fromCardEmbedMetadata.- 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 |