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.
npm install react-native-sized-webview


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 /empty 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, and onHeightChange callbacks.
- 🌲 ESM-first build, fully typed, sideEffects: false for optimal tree shaking.
- 📱 Verified on iOS, Android, and Expo Go out of the box.
``sh`
yarn add react-native-sized-webview react-native-webviewor
npm install react-native-sized-webview react-native-webview
No native steps are needed beyond the upstream react-native-webview dependency.
`tsx
import { SizedWebView } from 'react-native-sized-webview';
const Article = () => (
source={{
html:
Generated by your CMS and sized automatically ✨
,
}}
containerStyle={{ borderRadius: 12, overflow: 'hidden' }}
onHeightChange={(height) => console.log('content height', height)}
/>
);
`🧪 Example App
`sh
yarn
yarn example ios # or yarn example android
`The 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.⚙️ API
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
|
minHeight | number | 0 | Minimum height (dp) applied to the container to avoid layout jumps before content loads. |
| containerStyle | StyleProp | — | 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.🧩 Edge Cases Covered
- Trailing
and empty 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.requestAnimationFrame
- Height calculations are debounced via and a short idle timer to prevent resize storms.postMessage
- Measurements arrive through , then useAutoHeight coalesces 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 |postMessage
| --- | --- | --- |
| Initial render layout shifts | Requires timers / manual height guesswork | Zero shifts; height resolved before paint |
| React state updates on content change | Manual plumbing | Automatic bridge with RAF + debounce guard |ScrollView
| Scrolling in parent | 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.
`sh`
yarn test
Jest runs with full coverage collection and enforces 100% statements, branches, functions, and lines across the TypeScript source.
`sh``
yarn
yarn lint
yarn typecheck
yarn test --watch=false
yarn example ios # or yarn example android
This project uses react-native-builder-bob for packaging and release-it for publishing.
- Development workflow
- Sending a pull request
- Code of conduct
> [!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