Overview
This guide shows you how to embed your custom financial widget into your application using an iframe. Your widget is accessible via a single URL that handles data fetching, theming, and emits real-time events back to your application.
What you can do:
Embed the widget using a simple iframe
Configure language, theme, security identifiers, and user behavior
Listen to widget events (load, resize, user interactions, errors)
Handle security selections in your application (when enabled)
Auto-resize the widget based on content
Quick start
Basic iframe integration with auto-resize:
<iframe
id="widget"
src="https://embeded.bridgewise.com/en-US/TENANT_NAME/WIDGET_NAME?accessToken=YOUR_TOKEN&identifier=IDENTIFIER"
style="width:100%; border:0; height:400px;"
>
</iframe>
<script>
window.addEventListener("message", function (event) {
if (event.origin !== "https://embeded.bridgewise.com") return;
try {
const message = JSON.parse(event.data);
if (message.event && message.data && message.data.height) {
document.getElementById("widget").style.height =
message.data.height + "px";
}
} catch (e) {
// Ignore invalid messages
}
});
</script>
URL structure
Base path uses path params and query params:
Path: /:language/:tenant/:widgetName
Query: ?accessToken=...&identifier=...&identifierType=...&mode=...&actionType=...&sessionId=...&userId=...
Example:
https://embeded.bridgewise.com/he-IL/YOUR_TENANT/stock-report-page?accessToken=YOUR_TOKEN&identifier=AAPL-NASDAQ&mode=lightoo&actionType=external
Important:
Replace YOUR_TOKEN and AAPL_NASDAQ with your actual access token.
To start integrating Bridgewise widgets, you’ll need an access token. Please follow the instructions in our Authentication Guide to generate your token.
Properties (configuration)
All configurable parameters accepted by the iframe. “Path” refers to the URL path segment; “Query” refers to the querystring.
Bridget Widget Specific Properties
The following properties are specific to Bridget widgets and only apply when using Bridget-related widgets:
Notes:
Stock widgets accept identifiers and may require identifierType depending on your identifier format.
Fund widgets accept identifiers and may require identifierType depending on your identifier format.
Bridget-specific properties only apply to Bridget widgets and are ignored by other widget types.
Selection behavior (actionType)
default: Selection will redirect to the report page on BridgeWise platform.
external: Widget emits on-security-select-<widgetName> to the host app; host should handle navigation.
prevent: Clicks are ignored for navigation and no selection event is emitted.
Exposed events (postMessage)
The iframe posts JSON-serialized messages to window.parent. Each message has the shape:
{ "event": "on-<name>-<widgetName>", "data": { ... } }
Events currently emitted:
Load/resize
on-load-<widgetName>: { height: number }
on-resize-<widgetName>: { height: number }
Interaction
on-click-<widgetName>: { id: string, ...widgetSpecific }
on-hover-<widgetName>: { id: string, ...widgetSpecific }
on-link-click-<widgetName>: { url: string, text?: string } (when onLinkClick callback is provided)
Selection (emitted only when actionType=external)
on-security-select-<widgetName>: { id: string, ticker?: string, exchange?: string, type: 'fund' | 'stock' }
Bridget Teaser
on-bridget-teaser-trigger-<widgetName>: { companyId: number | null, message: string, primaryTickerSymbol: string | null }
Status
on-success-<widgetName>: { height: number }
on-error-<widgetName>: { message: string, status: number, data?: unknown, height: number }
Example event handling:
window.addEventListener("message", function (event) {
if (event.origin !== "https://embeded.bridgewise.com") return;
const message = JSON.parse(event.data);
console.log("Widget event:", message.event, message.data);
});
Handle user interactions (optional)
To capture when users click on securities in the widget:
window.addEventListener("message", function (event) {
if (event.origin !== "https://embeded.bridgewise.com") return;
try {
const message = JSON.parse(event.data);
// Auto-resize
if (message.data && message.data.height) {
document.getElementById("widget").style.height =
message.data.height + "px";
}
// Handle security selection (requires actionType=external)
if (message.event && message.event.includes("security-select")) {
const { id, ticker, exchange } = message.data;
console.log("User selected:", ticker, exchange);
// Redirect to your security page or open modal
}
// Handle Bridget teaser trigger
if (message.event && message.event.includes("bridget-teaser-trigger")) {
const {
companyId,
message: teaserMessage,
primaryTickerSymbol,
} = message.data;
console.log("Bridget teaser triggered:", {
companyId,
teaserMessage,
primaryTickerSymbol,
});
// Handle Bridget teaser interaction (e.g., show chat modal, navigate to AI insights)
},
// Handle link clicks (when onLinkClick callback is provided)
if (message.event && message.event.includes("link-click")) {
const { url, text } = message.data;
console.log("Link clicked:", { url, text });
// Handle link click (e.g., open in new tab, navigate, or prevent default)
// You can call your onLinkClick callback function here
if (window.handleLinkClick) {
window.handleLinkClick(url, text);
}
}
} catch (e) {
// Ignore invalid messages
}
});
Theming and styling
Set mode=dark to enable dark theme; omit or set mode=light for light theme.
The host page should set iframe { width: 100%; border: 0; } and rely on resize events to set height.
Localization
Provide the language path param (e.g., en-US, he-IL). Widgets may internally fall back to a default language where needed.
Error handling
Invalid tenant or widget names render a friendly error screen inside the iframe.
Missing accessToken produces an error; listen for on-error-<widgetName> to surface it in the host app.
Security considerations
Always validate event.origin in your message listener to accept only your iframe host domain.
accessToken is passed via querystring; prefer short-lived tokens over long-lived ones.
iOS Integration (SwiftUI/UIKit)
For iOS apps, you can embed the widget using WKWebView with proper postMessage handling:
import SwiftUI
import WebKit
struct WidgetView: UIViewRepresentable {
@Binding var height: CGFloat
let widgetURL: String
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> WKWebView {
let configuration = WKWebViewConfiguration()
let userContentController = WKUserContentController()
// Set up message handler for postMessage bridge
userContentController.add(context.coordinator, name: "postMessage")
// JavaScript bridge to capture iframe postMessage events
let script = """
window.addEventListener('message', function(event) {
if (event.origin !== 'https://embeded.bridgewise.com') return;
window.webkit.messageHandlers.postMessage.postMessage(event.data);
}, false);
"""
let userScript = WKUserScript(source: script, injectionTime: .atDocumentStart, forMainFrameOnly: false)
userContentController.addUserScript(userScript)
configuration.userContentController = userContentController
let webView = WKWebView(frame: .zero, configuration: configuration)
webView.navigationDelegate = context.coordinator
webView.scrollView.isScrollEnabled = false
if let url = URL(string: widgetURL) {
webView.load(URLRequest(url: url))
}
return webView
}
func updateUIView(_ webView: WKWebView, context: Context) {}
class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
var parent: WidgetView
init(_ parent: WidgetView) {
self.parent = parent
}
func userContentController(_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage) {
// Parse message from iframe
var messageData: [String: Any]?
if let jsonString = message.body as? String,
let jsonData = jsonString.data(using: .utf8) {
messageData = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any]
} else if let directObject = message.body as? [String: Any] {
messageData = directObject
}
guard let messageDict = messageData,
let event = messageDict["event"] as? String,
let data = messageDict["data"] as? [String: Any] else { return }
// Handle resize events
if event.contains("resize") || event.contains("load") || event.contains("success") {
if let height = data["height"] as? NSNumber {
DispatchQueue.main.async {
self.parent.height = CGFloat(height.doubleValue)
}
}
}
// Handle other events (security selection, errors, etc.)
if event.contains("security-select") {
// Handle security selection
print("Security selected: \(data)")
}
}
}
}
// Usage in SwiftUI
struct ContentView: View {
@State private var widgetHeight: CGFloat = 400
var body: some View {
WidgetView(
height: $widgetHeight,
widgetURL: "https://embeded.bridgewise.com/en-US/YOUR_TENANT/stock-report-page?accessToken=YOUR_TOKEN&identifier=AAPL-Nasdaq&mode=light"
)
.frame(height: widgetHeight)
}
}
Key points for iOS:
Use WKWebView with WKUserContentController for postMessage handling
Inject JavaScript to bridge iframe postMessage to webkit messageHandler
Handle height changes to dynamically resize the widget
Validate message origin for security
Disable WebView scrolling if you want the widget to control its own height
Android Integration (WebView)
For Android apps, use WebView with JavaScript interface to handle postMessage events:
// Widget WebView Component
class WidgetWebView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : WebView(context, attrs) {
private var heightChangeListener: ((Int) -> Unit)? = null
private var eventListener: ((String, Map<String, Any>) -> Unit)? = null
init {
setupWebView()
}
@SuppressLint("SetJavaScriptEnabled")
private fun setupWebView() {
settings.apply {
javaScriptEnabled = true
domStorageEnabled = true
loadWithOverviewMode = true
useWideViewPort = true
}
// Add JavaScript interface for postMessage handling
addJavascriptInterface(PostMessageHandler(), "AndroidInterface")
webViewClient = object : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
// Inject JavaScript to capture iframe postMessage
val script = """
window.addEventListener('message', function(event) {
if (event.origin !== 'https://embeded.bridgewise.com') return;
try {
var messageData = typeof event.data === 'string'
? event.data
: JSON.stringify(event.data);
AndroidInterface.handlePostMessage(messageData);
} catch (e) {
console.error('Error handling postMessage:', e);
}
}, false);
"""
evaluateJavascript(script, null)
}
}
}
fun setOnHeightChangeListener(listener: (Int) -> Unit) {
heightChangeListener = listener
}
fun setOnEventListener(listener: (String, Map<String, Any>) -> Unit) {
eventListener = listener
}
fun loadWidget(
language: String = "en-US",
tenant: String,
widgetName: String,
accessToken: String,
identifier: String,
mode: String = "light",
actionType: String = "external"
) {
val url = "https://embeded.bridgewise.com/$language/$tenant/$widgetName?" +
"accessToken=$accessToken&identifier=$identifier&mode=$mode&actionType=$actionType"
loadUrl(url)
}
inner class PostMessageHandler {
@JavascriptInterface
fun handlePostMessage(messageData: String) {
try {
val json = JSONObject(messageData)
val event = json.getString("event")
val data = json.getJSONObject("data")
// Handle height changes
if (event.contains("resize") || event.contains("load") || event.contains("success")) {
if (data.has("height")) {
val height = data.getInt("height")
post { heightChangeListener?.invoke(height) }
}
}
// Convert JSONObject to Map for event listener
val dataMap = mutableMapOf<String, Any>()
data.keys().forEach { key ->
dataMap[key] = data.get(key)
}
post { eventListener?.invoke(event, dataMap) }
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
// Usage in Activity/Fragment
class MainActivity : AppCompatActivity() {
private lateinit var widgetWebView: WidgetWebView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
widgetWebView = WidgetWebView(this)
// Handle height changes
widgetWebView.setOnHeightChangeListener { height ->
val layoutParams = widgetWebView.layoutParams
layoutParams.height = (height * resources.displayMetrics.density).toInt()
widgetWebView.layoutParams = layoutParams
}
// Handle widget events
widgetWebView.setOnEventListener { event, data ->
when {
event.contains("security-select") -> {
// Handle security selection
val ticker = data["ticker"] as? String
val exchange = data["exchange"] as? String
println("Security selected: $ticker-$exchange")
}
event.contains("error") -> {
// Handle errors
val message = data["message"] as? String
println("Widget error: $message")
}
}
}
// Load widget
widgetWebView.loadWidget(
tenant = "YOUR_TENANT",
widgetName = "stock-report-page",
accessToken = "YOUR_TOKEN",
identifier = "AAPL-Nasdaq"
)
setContentView(widgetWebView)
}
}
Key points for Android:
Enable JavaScript and DOM storage in WebView settings
Use @JavascriptInterface to create a bridge for postMessage handling
Inject JavaScript after page load to capture iframe messages
Convert pixel heights using display density for proper sizing
Handle events on the main thread using post()
Validate message origin in JavaScript for security
Mobile Integration Notes
For both iOS and Android:
The widget automatically handles responsive design for mobile viewports
Use actionType=external to handle security selections in your app instead of redirecting
Listen for on-error-* events to handle network or authentication issues gracefully
Consider implementing loading states while the widget initializes
Test with different device orientations and screen sizes
Security considerations:
Always validate the origin of postMessage events
Use HTTPS for all widget URLs
Store access tokens securely (iOS Keychain, Android EncryptedSharedPreferences)
Consider implementing token refresh logic for long-lived sessions
Notes
Event names are suffixed with the widgetName to let you distinguish multiple iframes on the same page.
Additional events like language fallbacks may be introduced; follow the on-*-<widgetName> convention.
Was this article helpful?
That’s Great!
Thank you for your feedback
Sorry! We couldn't be helpful
Thank you for your feedback
Feedback sent
We appreciate your effort and will try to fix the article