Skip to main content

Embedding in native WebViews

The embed document works in React Native's WebView (and other native WebViews) without a shim: the widget detects the native bridge and speaks the same protocol over it. The document is a build artifact — generate it once with buildEmbedDocument(), commit it to the app, and load it from local assets so charts work offline. If the app only renders some chart types, say so — buildEmbedDocument({ components: ['Line', 'Donut'] }) — and the committed document carries a bundle half the size.

What the widget does differently under React Native

  • Outgoing messages go through window.ReactNativeWebView.postMessage (as JSON strings — the bridge doesn't carry structured clones) instead of window.parent.postMessage.
  • Incoming messages are accepted as strings or objects, on both window and document — React Native delivers 'message' on document on Android and on window on iOS.

Nothing else changes: the message shapes are identical to the iframe protocol.

Wiring it up

import { WebView } from 'react-native-webview'

const chartHtml = require('./assets/unovis-widget.html') // buildEmbedDocument() output

function Chart ({ spec, theme }: { spec: ChartSpec; theme: 'light' | 'dark' }): JSX.Element {
const webview = useRef<WebView>(null)
const send = (message: object): void =>
webview.current?.postMessage(JSON.stringify(message))

return (
<WebView
ref={webview}
source={chartHtml}
originWhitelist={['*']}
onMessage={(event) => {
const message = JSON.parse(event.nativeEvent.data)
if (message.type === 'unovis:ready') send({ type: 'unovis:render', spec: { ...spec, theme }, options: { events: true } })
if (message.type === 'unovis:size') setHeight(message.height)
if (message.type === 'unovis:event') onChartTap(message.datum)
}}
/>
)
}

Theme changes without a reload

A color-scheme flip used to mean remounting the WebView. Send the theme instead — the widget re-renders its last spec and restyles the page background:

send({ type: 'unovis:theme', theme: colorScheme === 'dark' ? 'dark' : 'light' })

Sent before any chart has rendered, it just restyles the empty page, so it's safe to fire from a useColorScheme effect unconditionally.

Version pinning

Because the document is committed to the app, assert compatibility at startup: unovis:ready carries { version, specVersion } (see Chart spec → Versioning). Refuse to render — or prompt for an app update — when specVersion is newer than the specs the app builds.

Security posture

The bundle contains no fetch, XMLHttpRequest, WebSocket or dynamic imports, so the WebView needs no network access at all. Combined with local-asset loading, the chart surface adds zero egress.

Touch behavior

Interaction events (events: true) report taps the same way clicks are reported in a browser. Two knowns: lines and areas have no per-datum element (the crosshair is their readout — it tracks touch-drag), and tap handlers become active within ~500ms of the render settling.