Appearance
Integration Guide
This guide covers how to embed the CFC iframe in your application. The SDK supports two integration modes configured via a single mode parameter — the setup and API are otherwise identical.
Setup
typescript
import { CfcClient, Environment, IntegrationMode, NavigationTarget } from "@melio-eng/cfc-sdk";
const client = new CfcClient({
container: document.getElementById("cfc-container")!,
environment: Environment.STAGING,
token: authToken,
tenant: "my-tenant",
target: NavigationTarget.VIEW_PAYMENTS,
mode: IntegrationMode.FULL_PAGE,
onError: (err) => console.error("CFC error:", err),
});
client.init();The SDK creates the iframe inside your container, constructs the auth URL, and sets up all message handling automatically.
Integration Modes
The mode parameter controls how the iframe is rendered. The key distinction is who owns the vertical scroll:
- Full Page — CFC owns the full vertical space within its container. There is no host content above or below the iframe, so there is no double-scroll issue. The host app can still have a sidebar, header, or other chrome around the iframe — as long as there is no competing vertical content, this is full-page mode.
- Partial — The host has its own content above or below the CFC iframe (e.g., a hero section, footer, or other widgets in the same scrollable area). This creates a shared vertical scroll, so the SDK takes over scroll coordination and dynamically resizes the iframe height.
Full Page
In full-page mode, the iframe is set to height: 100% and width: 100%. Use this when CFC occupies the main content area — even if the host has a sidebar or header alongside it.
typescript
const client = new CfcClient({
// ...
mode: IntegrationMode.FULL_PAGE,
});html
<!-- Full page with sidebar + header — still full page mode -->
<div id="app" style="display: flex;">
<aside style="width: 240px;"><!-- Host sidebar --></aside>
<div style="flex: 1; display: flex; flex-direction: column;">
<header style="height: 64px;"><!-- Host header --></header>
<div id="cfc-container" style="flex: 1;">
<!-- CFC iframe fills remaining space — no double scroll -->
</div>
</div>
</div>Partial
In partial mode, the iframe is created with scrolling="no" and width: 100%. The SDK automatically resizes the iframe height based on CONTENT_SIZE_CHANGE messages from CFC, so it fits naturally within the host's scrollable content.
Use headerHeight to specify the height of host content above the iframe (e.g., a fixed header). CFC uses this value to correctly position floating elements like modals, tooltips, and action sheets inside the iframe. Without it, CFC assumes the iframe starts at the top of the viewport and these elements will appear offset by the height of your header.
typescript
const client = new CfcClient({
// ...
mode: IntegrationMode.PARTIAL,
headerHeight: 64, // height in pixels of host content above the iframe
});html
<!-- Partial — host content above and below CFC -->
<div id="app">
<header style="height: 64px;"><!-- Host header --></header>
<section><!-- Some host content above CFC --></section>
<div id="cfc-container">
<!-- CFC iframe embedded within scrollable host content -->
</div>
<footer><!-- Host footer --></footer>
</div>Auth URL
When client.init() is called, the SDK constructs an auth URL based on the environment:
https://partnerships.public-qa.melioservices.com/cfc/auth?token=<token>&tenant=<tenant>&target=/view-paymentsThe iframe src is set to this URL. CFC authenticates the token and redirects to the requested target page within the iframe.
How Dimension Sync Works
The SDK handles all dimension and scroll synchronization automatically in both modes.
Incoming from CFC
CONTENT_SIZE_CHANGE — CFC reports its content dimensions. The SDK resizes the iframe to match:
iframe.style.height = `${height}px`
iframe.style.width = `${width}px` // if providedSCROLL_TO — CFC requests the host to scroll (e.g., when a modal opens). The SDK scrolls to the requested position relative to the iframe and manages overflow: hidden/auto on the document.
Outgoing to CFC
DIMENSIONS_CHANGED — The SDK sends viewport and iframe position data on:
- CFC
LOADEDevent (initial) - Window
resize visualViewportresize (mobile keyboard)
USER_SCROLL — The SDK forwards the host's scroll position on every scroll event so CFC can position fixed elements correctly.
Event Callbacks
Loaded
CFC emits LOADED when it has finished loading and is ready. Use onLoaded to hide a loading spinner or trigger initialization logic:
typescript
const client = new CfcClient({
// ...
onLoaded: () => {
hideLoadingSpinner();
},
});Navigation
Use onNavigated to react to CFC navigation changes:
typescript
const client = new CfcClient({
// ...
onNavigated: (target, targetAction) => {
console.log(`CFC navigated to: ${target}`, targetAction);
},
});Payment Success
When a user successfully schedules a payment, CFC navigates to the payment success page and sends a NAVIGATED_TO_TARGET event with targetAction: 'schedulePaymentSuccess'. The SDK detects this automatically and calls onPaymentSuccess.
typescript
const client = new CfcClient({
// ...
onPaymentSuccess: () => {
showToast("Payment scheduled successfully!");
},
});Typical use cases:
- Show a survey after successful payment scheduling
- Update the host UI to reflect the new payment
- Track analytics events
- Trigger custom workflows
INFO
Under the hood, onPaymentSuccess fires when onNavigated receives targetAction: 'schedulePaymentSuccess'. Using onPaymentSuccess is the recommended approach — it's more explicit and doesn't require inspecting targetAction yourself.
Exit Handling
CFC emits EXIT_REQUESTED when the user wants to leave the CFC experience:
typescript
const client = new CfcClient({
// ...
onExitRequested: (originator) => {
console.log(`Exit requested by: ${originator}`);
client.destroy();
window.location.href = "/dashboard";
},
});Error Handling
Handle authentication errors and session expiry:
typescript
const client = new CfcClient({
// ...
onAuthenticationError: (reason, message) => {
console.error(`Auth error [${reason}]: ${message}`);
window.location.href = "/login";
},
onSessionExpired: () => {
client.destroy();
refreshTokenAndReinit();
},
});See Error Handling for more details on error reasons.
Session Keep-Alive
CFC sends USER_ACTIVE_PING when the user is active in the iframe. Use this to extend the host application session:
typescript
const client = new CfcClient({
// ...
onUserActivePing: () => {
resetSessionTimer();
},
});Programmatic Navigation
Navigate CFC to a specific target:
typescript
import { NavigationTarget } from "@melio-eng/cfc-sdk";
client.navigateTo(NavigationTarget.VIEW_PAYMENTS);
client.navigateTo(NavigationTarget.viewBill("bill-123"));See Navigation Targets for the full list.
Full Example
typescript
import { CfcClient, Environment, IntegrationMode, NavigationTarget } from "@melio-eng/cfc-sdk";
function initCfc(token: string) {
const container = document.getElementById("cfc-container")!;
const client = new CfcClient({
container,
environment: Environment.STAGING,
token,
tenant: "my-tenant",
target: NavigationTarget.VIEW_PAYMENTS,
mode: IntegrationMode.PARTIAL,
headerHeight: 64,
onLoaded: () => hideLoadingSpinner(),
onNavigated: (target, action) => {
analytics.track("cfc_navigation", { target, action });
},
onPaymentSuccess: () => showToast("Payment successful!"),
onExitRequested: () => {
client.destroy();
window.location.href = "/";
},
onAuthenticationError: (reason) => {
console.error("Auth failed:", reason);
client.destroy();
window.location.href = "/login";
},
onSessionExpired: () => {
client.destroy();
refreshTokenAndReinit();
},
onUserActivePing: () => resetSessionTimer(),
onError: (err) => console.error("CFC error:", err),
});
client.init();
return () => client.destroy();
}Cleanup
Always call destroy() when the integration is torn down (e.g., on route change in a SPA):
typescript
client.destroy();This removes the iframe from the DOM, cleans up all event listeners (message, resize, scroll, visualViewport), and clears all internal state.