Embedding and WebView integration
Technical reference for embedding OmniLab in a web page, mobile WebView, or custom domain — with HTML, JavaScript, and React Native examples.
This guide covers the technical implementation for all embedded OmniLab delivery modes: manual iframe, JavaScript player tag, native WebView (React Native and other platforms), and custom domain delegation via DNS.
See the business-facing overviews at:
Embed-ready URLs
All embedded delivery modes start from the same OmniLab URL patterns:
| Use case | URL pattern |
|---|---|
| Campaign landing page | https://experience.example.com/<campaign-public-link>?embedded=1 |
| Direct touchpoint | https://experience.example.com/<campaign-public-link>?c=<touchpoint-id>&embedded=1 |
| Space-specific launch | https://experience.example.com/<campaign-public-link>?s=<space-id>&embedded=1 |
| Variant or language | Append &v=<variant-id> or &l=<language> as needed |
Always add embedded=1 when loading in an iframe or WebView. This flag tells OmniLab to run in an embedded context and adjusts layout and navigation accordingly.
Manual iframe
Minimal example
<iframe
id="omnilab-embed"
src="https://experience.example.com/<campaign-public-link>?c=<touchpoint-id>&embedded=1"
title="OmniLab campaign"
loading="lazy"
allow="camera; microphone; geolocation"
style="width:100%;min-height:900px;border:0;overflow:hidden"
></iframe>Adjust the allow attribute to match the permissions the experience requires. Camera, microphone, and geolocation are only needed when the experience uses scanning, media capture, or location-aware flows.
Automatic height resize
OmniLab pages send postMessage resize events. Listen for them on the parent page so the iframe height follows content instead of showing an internal scrollbar.
<script>
window.addEventListener("message", (event) => {
const data = event.data;
if (data?.type !== "resize" || data?.target !== "body") return;
const iframe = document.querySelector("#omnilab-embed");
if (iframe && typeof data?.data?.height === "number") {
iframe.style.height = data.data.height + "px";
}
});
</script>Other postMessage events
Beyond resize, OmniLab sends two more message types to the parent page around a modal's lifecycle:
| Event type | When it fires |
|---|---|
resize | Content height changes |
open-modal | A modal is about to open inside the experience |
close-modal | A modal has closed |
A modal also waits to hear back from the parent page before it animates. If your wrapper needs to control the timing — for example to finish its own transition first — send these messages to the embedded experience:
| Message to send | What it tells the embedded experience |
|---|---|
ready-to-open-modal | The parent is ready, so the modal can animate in |
ready-to-close-modal | The parent is ready, so the modal can animate out |
Most wrappers can ignore these two and let the modal animate on its own; only send them if you need to hold the modal open or closed until your own UI catches up.
To log all messages during development:
window.addEventListener("message", (event) => {
console.log("OmniLab message", event.origin, event.data);
});JavaScript player tag
Use the OmniLab JavaScript player tag when a single host page must load different campaigns based on the current page URL.
<div id="omnilab-container"></div>
<script>
(function (b, o, n, u, s) {
var a, t;
a = b.createElement(u);
a.async = 1;
a.src = s;
t = b.getElementsByTagName(u)[0];
t.parentNode.insertBefore(a, t);
o[n] = o[n] || [];
})(document, window, "_om_async", "script", "https://<omnilab-script-host>/omplayer.js");
_om_async.push([
"init",
{
domain: "experience.example.com",
targetId: "omnilab-container",
queryParam: "id",
},
]);
</script>With that setup, the host page URL drives which OmniLab experience loads:
- Simple campaign:
https://www.example.com/promo?id=summer-campaign - With touchpoint:
https://www.example.com/promo?id=summer-campaign%3Fc%3D<touchpoint-id>%26embedded%3D1
The value in id is the path after the OmniLab domain. URL-encode any nested query string before publishing the link. If your deployment uses a specific script host URL, use that instead of the placeholder above.
React Native WebView
Installation
npm install react-native-webviewMinimal example
import React from "react";
import { WebView } from "react-native-webview";
export default function OmniLabWebView() {
return (
<WebView
source={{
uri: "https://experience.example.com/<campaign-public-link>?c=<touchpoint-id>&embedded=1",
}}
style={{ flex: 1 }}
javaScriptEnabled
/>
);
}Use a campaign landing page URL when the app should show multiple experiences, or a direct touchpoint URL to launch one specific experience immediately.
React Native requirements
- Enable JavaScript (
javaScriptEnabled={true}). - Grant camera, microphone, geolocation, and file permissions when the experience needs them.
- Use a full-screen or edge-to-edge container (
flex: 1). - If the host app must react to OmniLab events, confirm that the WebView bridge supports
postMessage. - Test virtual keyboard behaviour when the experience includes forms or sign-in steps.
Native iOS and Android guidance
- Use the platform's standard WebView component or a maintained wrapper library.
- Enable JavaScript.
- Forward device permission prompts (camera, microphone, geolocation) to the WebView.
- Test portrait and landscape layouts on real target devices.
- Verify app backgrounding, resume, and network loss do not break the participant flow.
- If the wrapper needs to react to
postMessageevents, validate the bridge in staging before shipping.
Custom domain delegation (DNS setup)
Overview
A delegated custom domain lets OmniLab experiences run under your own branded subdomain (for example experience.example.com) instead of the default OmniLab-hosted domain.
DNS record to create
Your DNS team creates a CNAME record pointing the chosen subdomain to the OmniLab endpoint:
| Record type | Name | Value |
|---|---|---|
| CNAME | experience (your chosen subdomain) | OmniLab endpoint provided by your Customer Success Manager |
If your DNS provider does not support CNAME at the apex (root domain), use a subdomain such as experience.example.com rather than trying to delegate example.com itself.
TTL considerations
Set the TTL (time-to-live) on the DNS record to a low value (300–600 seconds) before the switch so propagation is faster if you need to roll back. After the domain is stable, you can raise the TTL.
SSL certificate
OmniLab provisions an SSL certificate automatically once the DNS record is verified. Allow up to 15–30 minutes after DNS propagation for the certificate to become active. During this window, browsers may show a temporary certificate warning.
Propagation time
DNS changes can take up to 48 hours to propagate globally, depending on your provider and the existing TTL on the record. Plan the domain delegation well before your go-live date.
Typical embedding model
| Surface | Example |
|---|---|
| Host website | https://www.example.com/promo |
| Embedded OmniLab source | https://experience.example.com/summer-campaign?embedded=1 |
Go-live checklist
- Custom subdomain approved and DNS record created.
- DNS propagation verified (use a DNS checker tool).
- SSL certificate active (no certificate warning in browser).
- Standalone OmniLab URL working on the new hostname.
- Embedded page tested: layout, permissions, cookies, redirects.
- Existing QR codes and media placements updated to the new hostname.
Delivery best practices
- Let the iframe fill available width; avoid a fixed narrow desktop width.
- Start with a generous minimum height (
min-height: 900px), then let the resize listener set the final height. - Test sign-in steps, camera permissions, redirects, and mobile layout inside the real host page.
- If the host page uses sticky headers or constrained containers, test touchpoints that open drawers, forms, keyboards, or camera prompts.
- Keep secrets and API tokens on your backend — never in the kiosk page or WebView bundle.