Releases: mCodex/react-native-sized-webview
Release 1.3.0
🚀 react-native-sized-webview v1.3.0
A focused release that hardens the auto-height bridge, rebuilds the example app into a tour of real-world scenarios, and sharpens the docs. No breaking changes — drop-in upgrade from 1.2.x.
✨ Highlights
- 🛡️ Hardened bridge protocol — every message is now namespaced (
__RN_SIZED_WV__:<digits>) and validated by a strict regex; user-landpostMessagecan no longer mutate the container height. - 📐 No-wrapper measurement — the bridge no longer re-parents
<body>children or injects inline styles. It measures the page in place, preserving margin collapse and author CSS on margin-heavy CMS content. - 🔁 Dual-phase idempotent injection — the bridge runs at both
injectedJavaScriptBeforeContentLoadedandinjectedJavaScript, covering iOS WKWebView edge cases where the early hook is skipped on inlinesource.html. - 🧪 Frozen public handle —
window.__RN_SIZED_WEBVIEW__is non-writable, non-configurable, and exposes only{ version, refresh, destroy }. - 🎨 Brand-new example app — four scenario-focused demos, themed UI components, and React Compiler integration.
🛡️ Security & Correctness
- 🔒 Strict payload validation —
BRIDGE_NUMBER_PATTERNis now^\d+$(digits only), matching exactly what the bridge emits (String(Math.ceil(height))). Decimals, hex (0x100), exponential (1e10),NaN,Infinity, signed values, and whitespace-padded inputs are all rejected as forged input. - 🪟 Cross-frame
postMessagefilter — only same-window dispatches trigger work; arbitrary origins cannot ping the bridge. - 🧊 Frozen global handle —
Object.freeze+Object.defineProperty(writable: false, configurable: false)protect the public surface from page-script tampering. - 📏 Anomaly tracking + clamping — measurements above
MAX_REASONABLE_HEIGHT(120 000 dp) are retried and then clamped to the last known good height. - 🧹 Warm-up guard — sub-
WARMUP_MIN_HEIGHTfirst measurements are dropped (fixes iOS 26 WKWebView 1px collapse on tiny initial containers).
📐 Measurement (now O(k), not O(1))
Every measurement is the Math.max of multiple authoritative layout sources, without mutating the host page's DOM or styles:
body.scrollHeight/body.offsetHeightdocumentElement.scrollHeight/documentElement.offsetHeightlastInFlowChild.getBoundingClientRect().bottom + computedMarginBottom
Inert siblings (SCRIPT, STYLE, META, LINK, TITLE, HEAD, NOSCRIPT) and out-of-flow positions (fixed / sticky / absolute) are skipped during the last-child walk so they never short-circuit the probe.
💡 Why O(k)? k is the number of trailing inert/out-of-flow siblings (typically 0–2). Effectively constant in steady state; one layout flush per measurement.
🐛 Bug Fixes
- 🩹 Fixed systematic under-reporting on margin-heavy content. The previous synthetic
<div>wrapper broke margin collapse between the body and its first/last children, causing bothwrapper.scrollHeightandbody.scrollHeightto under-report. The bridge now reads the user's DOM directly. - 🍎 Fixed iOS WebView source-reload missing remount — added a
keyprop onSizedWebViewin the IntroDemo sosourcetoggles correctly remount the WebView. - 🎯 Fixed bridge not loading on iOS inline HTML — dual injection at both lifecycle hooks ensures the bridge always boots.
- 👀 Fixed late-reflow under-measurement —
ResizeObservernow observes bothdocument.bodyanddocument.documentElement. - ⏱️ Adaptive bootstrap-grace fallback — re-arms while either
pendingLoads > 0or within 5s of script start; refreshed bymarkLoading, fontloadingdone, andstate.refresh. Steady-state CPU cost: zero.
🎨 Example App Rebuild
The example app is now a modular tour of four scenarios:
- 🧩
IntroDemo— short ↔ extended HTML toggle showing dynamic re-measurement. - 🌐
RemoteSitePicker— full external websites loaded by URL. - 🔤
GoogleFontDemo— late web-font reflow +document.fonts.loadingdonehandling. - 📰
LongArticleDemo— long-form CMS content with margins, images, and headings.
Plus:
- 🎨 Reusable
PillButtonandSectionHeadercomponents on a shared theme (colors,spacing,radius). - 📦 Static HTML samples extracted into a dedicated
articleSamples.tsdata module. - ⚡ React Compiler integration via
babel-plugin-react-compiler.
📚 Documentation
- 📖 Rewritten README — clearer demo walkthrough, accurate "How It Works" pipeline (no more references to a wrapper that no longer exists), and explicit performance characteristics.
- 🧠 Spec-correct comments —
getBoundingClientRectis now described as viewport-relative (per CSSOM View) with the documented assumption that the host RN component setsscrollEnabled={false}. - ✍️ Copy-pasteable JSDoc examples — the
useAutoHeightexample now importsViewfromreact-native.
🧰 Internals
- ➖ Removed unused
RENDERABLE_MEDIA_TAGSconstant — smaller injected payload. - 🧼 Removed
applyBaseStyles(no more inline overrides of user CSS onbody/html). - 🧼 Removed
pruneTrailingNodes,hasMeaningfulText,hasRenderableContent,ensureWrapper, and thestate.wrapper/state.domDirtymachinery.
📦 Compatibility
- ✅ React Native 0.83+
- ✅ React 19+
- ✅ iOS 16+, Android API 24+
- ✅ Compatible with the React Compiler
⬆️ Upgrading
yarn add react-native-sized-webview@1.3.0
# or
npm install react-native-sized-webview@1.3.0No API changes. If you were importing the bridge string or hook directly:
import { View } from 'react-native';
import { WebView } from 'react-native-webview';
import { AUTO_HEIGHT_BRIDGE, useAutoHeight } from 'react-native-sized-webview';
function CustomSizedView({ html }: { html: string }) {
const { height, setHeightFromPayload } = useAutoHeight({ minHeight: 0 });
return (
<View style={{ height }}>
<WebView
source={{ html }}
injectedJavaScriptBeforeContentLoaded={AUTO_HEIGHT_BRIDGE}
injectedJavaScript={AUTO_HEIGHT_BRIDGE}
onMessage={(e) => setHeightFromPayload(e.nativeEvent.data)}
/>
</View>
);
}Release 1.1.1
🚀 react-native-sized-webview v1.1.1
This release improves auto-height reliability for real-world HTML content rendered inside React Native WebView, especially rich-text/editor output with links, paragraphs, trailing empty blocks, non-breaking spaces, and platform-specific WebView layout quirks.
🐛 Fixed
- Fixed cases where valid HTML content could be visually cut off because the injected height bridge undercounted the rendered document height.
- Improved handling of editor-generated trailing nodes such as
<p><br></p>without accidentally treating visible content as empty. - Fixed meaningful text detection for non-breaking spaces (
), which can render visible layout content but was previously treated like empty whitespace byString.prototype.trim().
✨ Improved
- Added a bounded rendered-content measurement pass that inspects actual DOM layout bounds as a fallback to
scrollHeight,offsetHeight, andclientHeight. - Height measurement now considers the wrapper,
body, andhtmlelements instead of relying only on the internal wrapper. - Improved resilience for margin collapsing, overflow, and WKWebView cases where standard height properties may underreport content size.
- Added a scan cap to keep measurement graceful on large documents and avoid expensive unbounded DOM traversal.
- Simplified the generated TypeScript declaration for the injected bridge to expose it as a
stringinstead of emitting a very large literal type.
💡 Why It Matters
Some WebView content can render beyond the height reported by common DOM properties. This is especially noticeable with rich HTML from CMS/editor sources, where trailing paragraphs, links, non-breaking spaces, and collapsed margins are common. The new measurement strategy follows the browser's rendered layout more closely, reducing clipped text while keeping the algorithm conservative and performant.
Release 1.1.0
Release 1.0.15
Release 1.0.14
1.0.14 (2025-11-14)
- Improve auto height bridge logic and update dependencies (17cdecd)
Full Changelog: v1.0.14...v1.0.14
Release 1.0.13
Release Notes
🚀 What’s New
- 📐 Reworked the bridge measurement pipeline to trust the dedicated wrapper first, eliminating runaway heights from
documentElementscroll math. - 🖼 Media now registers with a shared
ResizeObserverand usesdecode()/load events so late-loading images, iframes, and videos snap to the correct height without extra network fetches. - 🛡️ Added a sanity guard that retries and then clamps extreme height spikes (>120 000 px), keeping layout safe even when third-party markup misbehaves.
🔧 Quality & Stability
- 🔁 Fallback loop now resets gracefully—the bridge retries a few times, then falls back to the last good measurement instead of spamming
postMessage. - 🧼 Trailing
<br>/empty<p>trimming remains in place, but paired with smarter wrapper rebuilds so DOM rewrites stay rock solid. - 📄 README refreshed to document the new safety checks, media pipeline, and overall sizing strategy.
📚 Docs & DX
- ✍️ Updated highlights and architecture notes to match the modernized bridge so the README sells the current behavior clearly.
- 🧪 Example app stays focused on real-world scenarios after removing the temporary debug toggle.
Thanks to these changes, react-native-sized-webview now handles dynamic, rich content (and the occasional flaky CMS export) with a whole lot more confidence. Enjoy the smoother sizing! 💪
Full Changelog: v1.0.12...v1.0.13
Release 1.0.4
1.0.4 (2025-11-05)
- fix(autoHeightBridge): simplify height measurement, ensure body/html sizing, and extend update timeo (463b5e2)
Full Changelog: v1.0.3...v1.0.4
Release 1.0.3
1.0.3 (2025-11-05)
- fix(autoHeightBridge): use getMaxHeight for measurement and account for devicePixelRatio (cadfb67)
Full Changelog: v1.0.2...v1.0.3
Release 1.0.2
1.0.2 (2025-11-04)
- chore: improve JSDoc, tighten typings and update package keywords (99588fa)
Full Changelog: v1.0.1...v1.0.2
Release 1.0.1
1.0.1 (2025-11-03)
- chore(example): move react-native-safe-area-context dependency into example package.json (3c320fa)
- chore(package.json): remove src from packaged files and ignore .map artifacts (677726c)
- ci: add yarn prepare step to build-web job before exporting web example (f3c0915)
Full Changelog: v1.0.0...v1.0.1