React Native WebView that auto-sizes to match its HTML content—whether you load local HTML or full external websites—without manual measurements, timers, or layout flicker.
Important
⚡️ SizedWebView keeps the parent scroll view in charge by disabling the inner WebView scroll and syncing height changes via a lightweight bridge.
Tip
💡 Works out-of-the-box with dynamic CMS pages, FAQs, marketing landers, local HTML snippets, or full external sites.
- 📐 Wrapper-based measurement keeps the WebView content in a dedicated container, so height always reflects the real DOM footprint.
- 🚀 Modern pipeline powered by
ResizeObserver,MutationObserver,visualViewport, and font-load events with graceful fallbacks. - 🖼 Media aware: images, iframes, and videos schedule immediate + next-frame re-measures as soon as they finish loading.
- 🧼 Auto-prunes trailing
<br>/empty<p>tags that CMS editors often append, eliminating phantom spacing. - 🛡️ Sanity guard clamps runaway heights and retries with the last good value, so flaky pages never lock your layout.
- 🧵 Keeps the WebView scroll-disabled so outer
ScrollViews and gesture handlers stay silky smooth. - 🎨 Transparent background by default; style the container however you like.
- ⚙️ Friendly API with
minHeight,containerStyle, andonHeightChangecallbacks. - 🌲 ESM-first build, fully typed,
sideEffects: falsefor optimal tree shaking. - 📱 Verified on iOS, Android, and Expo Go out of the box.
yarn add react-native-sized-webview react-native-webview
# or
npm install react-native-sized-webview react-native-webviewNo native steps are needed beyond the upstream react-native-webview dependency.
import { SizedWebView } from 'react-native-sized-webview';
const Article = () => (
<SizedWebView
minHeight={180}
source={{
html: `
<html>
<body>
<h1>Privacy policy</h1>
<p>Generated by your CMS and sized automatically ✨</p>
</body>
</html>
`,
}}
containerStyle={{ borderRadius: 12, overflow: 'hidden' }}
onHeightChange={(height) => console.log('content height', height)}
/>
);yarn
yarn example ios # or yarn example androidThe example showcases:
- Auto-sizing dynamic HTML with toggled sections.
- Live external sites (Marvel, NFL, Google, Wikipedia, The Verge) embedded without layout thrash.
- Real-time height readouts so you can verify your own endpoints quickly.
- One code path that works the same on iOS, Android, and Expo Go.
Note
🧪 The demo is built with Expo; swap the uri to test your own pages instantly.
| Prop | Type | Default | Description |
|---|---|---|---|
minHeight |
number |
0 |
Minimum height (dp) applied to the container to avoid layout jumps before content loads. |
containerStyle |
StyleProp<ViewStyle> |
— | Styles applied to the wrapping View. Use it for padding, borders, or shadows. |
onHeightChange |
(height: number) => void |
— | Callback fired whenever a new height is committed. Great for analytics or debugging. |
...WebViewProps |
— | — | All remaining props are forwarded to the underlying react-native-webview. |
Note
🧩 scrollEnabled defaults to false so sizing remains deterministic. Only enable it if the WebView should manage its own scroll.
- Trailing
<br>and empty<p>tags are stripped automatically so CMS exports don’t leave phantom padding. - Images, iframes, and videos reschedule measurements the moment they finish loading—perfect for hero images at the end of an article.
- Wrapper rebuild + fallback timers keep measurements stable even if the remote page rewrites the entire DOM after load.
- Measurements above safe bounds are retried and then clamped to the last known good height, protecting against broken markup or third-party scripts.
- Injected bridge re-parents all body children into a dedicated wrapper, trims trailing blanks, and observes DOM mutations, layout changes, font loads, and viewport shifts.
- Media events (images / iframes / video) trigger immediate + next-frame samples so late assets still report accurate heights.
- Media elements stay observed via
ResizeObserver+ decode promises, catching intrinsic size changes without duplicate network requests. - Height calculations are debounced via
requestAnimationFrameand a short idle timer to prevent resize storms. - Measurements arrive through
postMessage, thenuseAutoHeightcoalesces them into a single render per frame. - Package exports the bridge, hook, and helpers individually, making it easy to build bespoke wrappers when needed.
| Scenario | Plain react-native-webview |
react-native-sized-webview |
|---|---|---|
| Initial render layout shifts | Requires timers / manual height guesswork | Zero shifts; height resolved before paint |
| React state updates on content change | Manual postMessage plumbing |
Automatic bridge with RAF + debounce guard |
Scrolling in parent ScrollView |
Nested scroll can fight gestures | Parent retains full momentum and gesture priority |
Benchmarks were captured on CMS articles up to 3k words in a 60 fps RN dev build. The bridge batches DOM mutations so even long documents resize without thrashing the JS thread.
yarn testJest runs with full coverage collection and enforces 100% statements, branches, functions, and lines across the TypeScript source.
yarn
yarn lint
yarn typecheck
yarn test --watch=false
yarn example ios # or yarn example androidThis project uses react-native-builder-bob for packaging and release-it for publishing.
Caution
🔬 Before submitting PRs that touch the bridge script, please test the example app on both iOS and Android to catch edge cases with interactive embeds.
MIT © Mateus Andrade