@videowisehq/videowise-react-native-sdk
v2.2.0
Published
Videowise SDK for React Native applications.
Readme
@videowisehq/videowise-react-native-sdk
Videowise SDK for React Native applications.
Installation
npm install @videowisehq/videowise-react-native-sdk react-native-webview @react-native-async-storage/async-storage react-native-videoFor Live Shopping PiP, react-native-video (>= 6.0.0) is required. Run pod install in your ios/ folder after installing.
SDK
interface VideowiseSDKProps {
Provider: React.FC<ProviderProps>;
Inline: React.FC<InlineProps>;
Floating: React.FC<FloatingProps>;
VideoFeed: React.FC<VideoFeedProps>;
LiveShoppingChannel: React.FC<LiveShoppingChannelProps>;
trackingPixel: (parameters: ParametersType) => Promise<Response | null>;
getStoredData: () => Promise<StoredData>;
}
const VideowiseSDK: VideowiseSDKProps = {
Provider,
Inline,
Floating,
VideoFeed,
LiveShoppingChannel,
trackingPixel,
getStoredData,
};Types
Event callbacks are typed using discriminated unions via OnEventType<T>:
| Callback | Type parameter | event.type | event.detail |
| ------------------------- | ------------------------------------- | ----------------- | ------------------------------------- |
| onEvent (widgets) | 'custom-event' | 'custom-event' | EventDetailType + name: EventType |
| onEvent (Live Shopping) | 'live-shopping' | 'live-shopping' | LSTrackingEventData |
| onAddToCart | 'add-to-cart' | 'add-to-cart' | EventDetailType |
| onCheckout | 'custom-event' or 'live-shopping' | matches component | matches component |
Key exported types (available from the compiled package):
InlineProps,FloatingProps,VideoFeedProps,LiveShoppingChannelProps,ProviderPropsOnEventType,EventDetailType,EventType,EventTypeNameLSTrackingEventData,LSTrackingProductSchemaType,LSWidgetInteractionType,LSHostEventTypeParametersType,OrderItem(fromlib/utils/tracking-pixel)
Shared props
These props are available on Inline, Floating, VideoFeed, and LiveShoppingChannel:
| Prop | Type | Required | Default | Description |
| ------------------ | --------------------------------------------- | -------- | -------------- | ----------------------------------------------------- |
| environment | 'local' | 'staging' | 'production' | No | 'production' | Environment target |
| loggedInUserData | { email?: string; fullName?: string } | No | — | Passes logged-in user info to the embed |
| onEvent | (event: OnEventType<T>) => void | No | — | Callback for custom events (T depends on component) |
| onAddToCart | (event: OnEventType<'add-to-cart'>) => void | No | — | Callback when a product is added to cart |
| onCheckout | (event: OnEventType<T>) => void | No | — | Callback for checkout events |
Videowise Live Shopping Channel
A full-screen live shopping channel component that embeds the Videowise live streaming experience. It renders a WebView with the live shopping channel and dispatches typed live-shopping events for user interactions like adding products to cart.
Note: The
LiveShoppingChannelcomponent does not need to be wrapped inside<VideowiseSDK.Provider>. It operates independently.
Props
| Prop | Type | Required | Default | Description |
| -------------------- | ----------------------------------------------- | -------- | -------------- | ----------------------------------------------------------------------- |
| videowiseInfo | object | Yes | — | Store configuration (see below) |
| lsId | string | No | — | ID of a specific live shopping stream |
| offsetY | number | No | 0 | Reduces the height of the component wrapper by screenHeight - offsetY |
| playerTopOffset | number | No | — | Top offset (px) for player UI elements |
| playerBottomOffset | number | No | — | Bottom offset (px) for player UI elements |
| vwStyle | string | No | — | Custom CSS injected into the live shopping embed |
| channelBodyHtml | string | No | monolith mount | Optional HTML for the WebView <body> (overrides default monolith) |
| enabledPipButton | boolean | No | true | Show the PiP button in the live shopping player (window.vwEnabledPipButton) |
| style | StyleProp<ViewStyle> | No | — | Custom style overrides for the component wrapper |
| loggedInUserData | { email?: string; fullName?: string } | No | — | Logged-in user info passed to the embed |
| onEvent | (event: OnEventType<'live-shopping'>) => void | No | — | Callback for live shopping events |
| onAddToCart | (event: OnEventType<'add-to-cart'>) => void | No | — | Callback when a product is added to cart |
| onCheckout | (event: OnEventType<'live-shopping'>) => void | No | — | Callback for checkout events |
| environment | 'local' | 'staging' | 'production' | No | 'production' | Environment target |
videowiseInfo object
| Property | Type | Required | Default | Description |
| -------- | -------- | -------- | ------- | -------------------------------------------------- |
| shop | string | Yes | — | Your store domain (e.g. storename.myshopify.com) |
Example
<VideowiseSDK.LiveShoppingChannel
videowiseInfo={{ shop: 'storename.myshopify.com' }}
lsId="your-live-stream-id"
environment="production"
offsetY={50}
playerTopOffset={44}
playerBottomOffset={34}
vwStyle=".vw-player { border-radius: 8px; }"
style={{ backgroundColor: '#000' }}
loggedInUserData={{ email: '[email protected]', fullName: 'Jane Doe' }}
onEvent={(event) => console.log('onEvent', event.detail.interactionType, event)}
onAddToCart={(event) => console.log('onAddToCart', event.detail)}
onCheckout={(event) => console.log('onCheckout', event)}
/>Live shopping events use LSTrackingEventData in event.detail, including fields like interactionType (LSWidgetInteractionType), eventType (LSHostEventType), lsId, item, and playback timing.
See the full list of live shopping events: https://docs.videowise.com/custom-events-ls
Custom channel body / modular section layout
By default the WebView body is a single monolith mount:
<div id="videowise_stream_channel"></div>Pass channelBodyHtml to replace that body content — for example to use modular channel sections with your own HTML between mounts. Omit #videowise_stream_channel when using sections (the monolith container wins and sections are ignored).
<VideowiseSDK.LiveShoppingChannel
videowiseInfo={{ shop: 'storename.myshopify.com' }}
channelBodyHtml={`
<div id="videowise_stream_channel_hero"></div>
<div id="videowise_stream_channel_livenow"></div>
<h2>Upcoming</h2>
<div id="videowise_stream_channel_upcoming"></div>
<h2>Past shows</h2>
<div id="videowise_stream_channel_past"></div>
`}
/>Mount ids: videowise_stream_channel_hero, videowise_stream_channel_livenow, videowise_stream_channel_upcoming, videowise_stream_channel_past, optional videowise_stream_channel_shared.
Full details: Live channel page layout
Live Shopping Picture-in-Picture (PiP)
When a user taps PiP inside the live shopping WebView, the SDK:
- Receives a
PIP_ENTERmessage from the embed with the HLS stream URL,lsId, and playback position - Swaps the WebView for a native
react-native-videoplayer and enters system PiP - On PiP exit, remounts the WebView with the same
lsIdso the embed reloads the stream; for recordings, also writes playback position to session storage so the web player can seek on resume - On iOS, automatically calls
VwAppMinimizer.minimize()when PiP becomes active (if the host app implements the native module)
Android typically backgrounds the app automatically when PiP starts.
Setup in your app
1. Install peer dependencies
npm install react-native-video
cd ios && pod install && cd ..2. Render LiveShoppingChannel
No extra PiP wiring is needed in JS — just render the component:
import VideowiseSDK from '@videowisehq/videowise-react-native-sdk';
export function LiveShoppingScreen() {
return (
<VideowiseSDK.LiveShoppingChannel
videowiseInfo={{ shop: 'storename.myshopify.com' }}
environment="production"
onEvent={(event) => console.log(event)}
onAddToCart={(event) => console.log(event)}
/>
);
}Pass lsId to open a specific stream on first load:
<VideowiseSDK.LiveShoppingChannel
videowiseInfo={{ shop: 'storename.myshopify.com' }}
lsId="your-live-stream-id"
/>Disable the PiP button if needed (enabled by default):
<VideowiseSDK.LiveShoppingChannel
videowiseInfo={{ shop: 'storename.myshopify.com' }}
enabledPipButton={false}
/>3. iOS — Info.plist
Add background audio so PiP playback continues when the app is backgrounded:
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>4. Android — AndroidManifest.xml
Enable PiP on your main activity:
<activity
android:name=".MainActivity"
android:supportsPictureInPicture="true"
...>5. Optional — iOS VwAppMinimizer (host app native module)
iOS has no public API to programmatically send the app to the background. The SDK calls VwAppMinimizer.minimize() automatically when PiP becomes active on iOS. Implement this native module in your host app (not in the SDK npm package) to enable auto-minimize.
App Store note: the example below uses a private API (
UIApplication.suspend). Use only if your team accepts that trade-off.
ios/YourApp/VwAppMinimizer.swift:
import UIKit
import React
@objc(VwAppMinimizer)
class VwAppMinimizer: NSObject {
@objc static func requiresMainQueueSetup() -> Bool {
true
}
@objc func minimize() {
DispatchQueue.main.async {
UIApplication.shared.perform(Selector(("suspend")))
}
}
}ios/YourApp/VwAppMinimizer.m:
#import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(VwAppMinimizer, NSObject)
RCT_EXTERN_METHOD(minimize)
@endRegister the files in your Xcode project
Creating the files is not enough — they must be registered in your app target so Xcode compiles them. You can do this in Xcode or by editing project.pbxproj directly.
Option A — Xcode UI
- Open
ios/YourApp.xcworkspacein Xcode - Right-click your app folder (same group as
AppDelegate.swift) → Add Files to "YourApp"... - Select
VwAppMinimizer.swiftandVwAppMinimizer.m - Ensure your app target is checked under Add to targets
Option B — Edit project.pbxproj
- Place both files in
ios/YourApp/next toAppDelegate.swift - Open
ios/YourApp.xcodeproj/project.pbxproj - Generate four unique IDs (run
uuidgenfour times, remove dashes, uppercase) - Add entries following the same pattern as
AppDelegate.swift:
/* PBXBuildFile — add to PBXBuildFile section */
SWIFT_BUILD_ID /* VwAppMinimizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = SWIFT_FILE_ID /* VwAppMinimizer.swift */; };
OBJC_BUILD_ID /* VwAppMinimizer.m in Sources */ = {isa = PBXBuildFile; fileRef = OBJC_FILE_ID /* VwAppMinimizer.m */; };
/* PBXFileReference — add to PBXFileReference section */
SWIFT_FILE_ID /* VwAppMinimizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = VwAppMinimizer.swift; path = YourApp/VwAppMinimizer.swift; sourceTree = "<group>"; };
OBJC_FILE_ID /* VwAppMinimizer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = VwAppMinimizer.m; path = YourApp/VwAppMinimizer.m; sourceTree = "<group>"; };- Add
SWIFT_FILE_IDandOBJC_FILE_IDto your app'sPBXGroupchildrenarray (same group that listsAppDelegate.swift) - Add
SWIFT_BUILD_IDandOBJC_BUILD_IDtoPBXSourcesBuildPhase→filesarray (Compile Sources)
Replace YourApp with your iOS app folder name. Use the code blocks above for the file contents and mirror how AppDelegate.swift is registered in your project.pbxproj.
If VwAppMinimizer is not registered, the SDK safely skips minimization — PiP still works.
6. Install the SDK in your app and rebuild
Install from npm in your React Native project (not a local file: link):
npm install @videowisehq/videowise-react-native-sdk react-native-webview @react-native-async-storage/async-storage react-native-video
cd ios && pod install && cd ..
npx react-native run-ios # physical iPhone/iPad required for PiP
npx react-native run-androidTesting notes
| Platform | Notes |
| -------- | ----- |
| iOS (physical device) | Required for PiP testing |
| iOS Simulator | PiP does not work — isPictureInPictureSupported() returns false |
| Android | PiP works on supported devices; app usually backgrounds automatically |
The floating PiP window always shows system playback controls (play/pause, close). Those cannot be removed by the SDK.
WebView → native PiP protocol
The live shopping embed sends this message when the user requests PiP:
{
"request": "PIP_ENTER",
"payload": {
"lsId": "live-shopping-stream-id",
"url": "https://your-cdn.example.com/live/stream.m3u8",
"startTime": 142.5
}
}| Field | Required | Description |
| ----- | -------- | ----------- |
| lsId | Yes | Used to restore the correct stream when PiP ends |
| url | Yes | HLS (.m3u8) live stream URL |
| startTime | No | Playback position in seconds where native PiP should start |
Native PiP → WebView resume
When PiP ends, the SDK remounts the WebView with window.lsId set so the embed auto-loads the same stream.
For recordings (not live streams), the SDK also writes the native player's current playback time to session storage:
sessionStorage.getItem('reeview_ls_session_recording_time');
// e.g. '{"time":187.4,"lsId":"live-shopping-stream-id"}'The embed reads this key on init for recordings only, parses { time, lsId }, and seeks the web player to resume from that position. Live streams do not use this key — they resume at the live edge.
Full SDK reference: docs.videowise.com/sdk/react-native
Note: The
Inline,Floating, andVideoFeedcomponents must be wrapped inside<VideowiseSDK.Provider>to function correctly. The Provider manages modal video state and handles navigation when a user taps on a video.
<VideowiseSDK.Provider>
{/* Your components here */}
</VideowiseSDK.Provider>Videowise VideoFeed
A full-screen video feed component that displays a single video with shopping capabilities. The video player is shown immediately upon render.
Props
| Prop | Type | Required | Default | Description |
| --------------------------- | ---------------------------------------------- | -------- | -------------- | ---------------------------------------------------------- |
| videowiseInfo | object | Yes | — | Store configuration (see below) |
| widgetId | string | Yes | — | The ID of the Videowise widget |
| style | StyleProp<ViewStyle> | No | — | Custom style overrides for the component wrapper |
| videoId | string | No | '' | Specific video ID to display |
| rightActionButtonsTop | number | No | 7 | Top offset (px) for the right action buttons (close, etc.) |
| marketingLogoTop | number | No | 20 | Top offset (px) for the marketing logo |
| shoppableTop | number | No | 0 | Top offset (px) for the shoppable product list |
| shoppableBottom | number | No | 16 / 20 | Bottom padding (px) for shoppable UI |
| shoppableCardListBottom | number | No | 0 | Bottom offset (px) for the shoppable card list |
| shoppableListAspectRatio | ${number}/${number}` | `"auto" | No | "1/1" | Aspect ratio of product list items |
| disableIndependentApiCall | boolean | No | true | Disables independent API calls |
| loggedInUserData | { email?: string; fullName?: string } | No | — | Logged-in user info passed to the embed |
| onEvent | (event: OnEventType<'custom-event'>) => void | No | — | Callback for custom events |
| onAddToCart | (event: OnEventType<'add-to-cart'>) => void | No | — | Callback when a product is added to cart |
| onCheckout | (event: OnEventType<'custom-event'>) => void | No | — | Callback for checkout events |
| environment | 'local' | 'staging' | 'production' | No | 'production' | Environment target |
videowiseInfo object
| Property | Type | Required | Default | Description |
| -------------- | ------------------------------------------------------ | -------- | ----------- | -------------------------------------------------- |
| shop | string | Yes | — | Your store domain (e.g. storename.myshopify.com) |
| cartType | 'Shopify' | 'Magento' | 'SFCC' | 'Tapcart' | 'Other' | No | 'Shopify' | Cart platform type |
| currency | string | No | 'USD' | Display currency |
| currencyRate | number | No | 1 | Currency conversion rate |
| productId | number | null | No | null | Filter by specific product ID |
Example
<VideowiseSDK.Provider>
<View style={{ flex: 1 }}>
<VideowiseSDK.VideoFeed
videowiseInfo={{
cartType: 'Shopify',
shop: 'storename.myshopify.com',
currency: 'USD',
currencyRate: 1,
productId: null,
}}
widgetId="your-widget-id"
environment="production"
rightActionButtonsTop={44}
marketingLogoTop={44}
shoppableTop={44}
shoppableBottom={44}
shoppableCardListBottom={44}
shoppableListAspectRatio="1/1"
videoId=""
onEvent={(event) => console.log('onEvent', event)}
onAddToCart={(event) => console.log('onAddToCart', event)}
onCheckout={(event) => console.log('onCheckout', event)}
/>
</View>
</VideowiseSDK.Provider>Videowise Floating
A floating widget overlay that appears at the bottom-left of the screen. It dynamically sizes itself based on the widget content. When a user taps on a video, a full-screen VideoFeed modal opens.
Props
| Prop | Type | Required | Default | Description |
| -------------------------- | ---------------------------------------------- | -------- | -------------- | ---------------------------------------------- |
| videowiseInfo | object | Yes | — | Store configuration (same as VideoFeed) |
| widgetId | string | Yes | — | The ID of the Videowise widget |
| rightActionButtonsTop | number | No | — | Top offset (px) for the right action buttons |
| marketingLogoTop | number | No | — | Top offset (px) for the marketing logo |
| shoppableTop | number | No | — | Top offset (px) for the shoppable product list |
| shoppableBottom | number | No | — | Bottom padding (px) for shoppable UI |
| shoppableCardListBottom | number | No | — | Bottom offset (px) for the shoppable card list |
| shoppableListAspectRatio | ${number}/${number}` | `"auto" | No | — | Aspect ratio of product list items |
| loggedInUserData | { email?: string; fullName?: string } | No | — | Logged-in user info passed to the embed |
| onEvent | (event: OnEventType<'custom-event'>) => void | No | — | Callback for custom events |
| onAddToCart | (event: OnEventType<'add-to-cart'>) => void | No | — | Callback when a product is added to cart |
| onCheckout | (event: OnEventType<'custom-event'>) => void | No | — | Callback for checkout events |
| environment | 'local' | 'staging' | 'production' | No | 'production' | Environment target |
Example
<VideowiseSDK.Provider>
<View style={{ flex: 1 }}>
<VideowiseSDK.Floating
videowiseInfo={{
cartType: 'Shopify',
shop: 'storename.myshopify.com',
currency: 'USD',
currencyRate: 1,
productId: null,
}}
widgetId="your-widget-id"
environment="production"
rightActionButtonsTop={44}
marketingLogoTop={44}
shoppableTop={44}
shoppableBottom={44}
shoppableListAspectRatio="1/1"
onEvent={(event) => console.log('onEvent', event)}
onAddToCart={(event) => console.log('onAddToCart', event)}
onCheckout={(event) => console.log('onCheckout', event)}
/>
</View>
</VideowiseSDK.Provider>Videowise Inline
An inline widget that embeds directly within the page flow. It starts with minimal height and dynamically expands as content loads, making it suitable for placing inside scrollable layouts. When a user taps on a video, a full-screen VideoFeed modal opens.
Available Widget Types
The following widget layouts are supported for the Inline component:
- Single Video
- In-Page Video Player
- Quick Shop Carousel
- Stories
- Video Carousel
- Highlighted Carousel
- Overlapping Carousel
- Scattered Videos
- Masonry Grid
- Grid Gallery
- Floating
- Gallery Highlight
Props
| Prop | Type | Required | Default | Description |
| -------------------------- | ---------------------------------------------- | -------- | -------------- | ---------------------------------------------- |
| videowiseInfo | object | Yes | — | Store configuration (same as VideoFeed) |
| widgetId | string | Yes | — | The ID of the Videowise widget |
| rightActionButtonsTop | number | No | — | Top offset (px) for the right action buttons |
| marketingLogoTop | number | No | — | Top offset (px) for the marketing logo |
| shoppableTop | number | No | — | Top offset (px) for the shoppable product list |
| shoppableBottom | number | No | — | Bottom padding (px) for shoppable UI |
| shoppableCardListBottom | number | No | — | Bottom offset (px) for the shoppable card list |
| shoppableListAspectRatio | ${number}/${number}` | `"auto" | No | — | Aspect ratio of product list items |
| loggedInUserData | { email?: string; fullName?: string } | No | — | Logged-in user info passed to the embed |
| onEvent | (event: OnEventType<'custom-event'>) => void | No | — | Callback for custom events |
| onAddToCart | (event: OnEventType<'add-to-cart'>) => void | No | — | Callback when a product is added to cart |
| onCheckout | (event: OnEventType<'custom-event'>) => void | No | — | Callback for checkout events |
| environment | 'local' | 'staging' | 'production' | No | 'production' | Environment target |
Example
<VideowiseSDK.Provider>
<View style={{ flex: 1, justifyContent: 'center' }}>
<VideowiseSDK.Inline
videowiseInfo={{
cartType: 'Shopify',
shop: 'storename.myshopify.com',
currency: 'USD',
currencyRate: 1,
productId: null,
}}
widgetId="your-widget-id"
environment="production"
rightActionButtonsTop={44}
marketingLogoTop={44}
shoppableTop={44}
shoppableBottom={44}
shoppableCardListBottom={50}
shoppableListAspectRatio="1/1"
onEvent={(event) => console.log('onEvent', event)}
onAddToCart={(event) => console.log('onAddToCart', event)}
onCheckout={(event) => console.log('onCheckout', event)}
/>
</View>
</VideowiseSDK.Provider>Tracking Pixel
VideowiseSDK.trackingPixel sends a conversion pixel after checkout. It reads the persisted Videowise user/session IDs from async storage (set automatically during live shopping sessions) and reports order data to the Videowise API.
Parameters
| Parameter | Type | Required | Description |
| --------------- | ------------- | -------- | ----------------------- |
| shop | string | Yes | Store domain |
| orderId | number | Yes | Order ID |
| orderTotal | number | Yes | Order total amount |
| orderCurrency | string | Yes | Order currency code |
| orderItems | OrderItem[] | Yes | Line items in the order |
Example
await VideowiseSDK.trackingPixel({
shop: 'storename.myshopify.com',
orderId: 1234567890,
orderTotal: 99.99,
orderCurrency: 'USD',
orderItems: [
{
id: 'line-item-id',
quantity: 1,
title: 'Product Name',
variant: {
id: 'variant-id',
image: { src: null },
price: { amount: 99.99, currencyCode: 'USD' },
product: {
id: 'product-id',
title: 'Product Name',
vendor: null,
type: null,
untranslatedTitle: null,
url: '/products/example',
},
sku: null,
title: 'Default',
untranslatedTitle: 'Default',
},
finalLinePrice: { amount: 99.99, currencyCode: 'USD' },
},
],
});Returns Promise<Response | null>. Returns null if required IDs or order data are missing, or if the request fails.
Stored Data
VideowiseSDK.getStoredData reads persisted Videowise values from async storage (set automatically when SDK components run). Use it when you need access to these IDs outside of trackingPixel — for example custom analytics or deep linking. It is a plain async function and does not need to be called inside VideowiseSDK.Provider.
Return value (StoredData)
| Property | Type | Description |
| -------- | ---------------- | --------------------------------------------------------------------------- |
| uid | string \| null | Videowise user ID (reeview_uid). null if missing or expired |
| lsId | string \| null | Live shopping session ID (reeview_lsid). null if missing or expired |
Additional fields may be added to this object in future SDK versions.
Example
const { uid, lsId } = await VideowiseSDK.getStoredData();