Lightning-fast, modern in-app browser for React Native powered by Nitro Modules. Enjoy direct JSI bindings, zero bridge overhead, and polished native browser UX on both iOS and Android.
- Highlights
- Platform Support
- Installation
- Usage
- Migration Guide (pre-Nitro → latest)
- API Surface
- Options Reference
- Dynamic Color Palettes
- Security & Emulator Notes
- Troubleshooting
- Contributing
- License
- TurboModule-first implementation with native-speed execution and zero bridge overhead.
- Fully typed TypeScript API plus ergonomic React hook helpers.
- âś… iOS 26 ready: high-contrast dynamic colors, edge-dismiss control, and UI style overrides.
- âś… Android 16 ready: partial custom tabs, referrer controls, and per-theme palettes.
- Authentication-first design with ephemeral sessions and graceful fallbacks.
- Works great with Hermes, Fabric, and the React Native New Architecture.
| Platform | Minimum | Targeted | Highlights |
|---|---|---|---|
| iOS | 11.0 | 17 / 26* | SafariViewController + ASWebAuthentication support. |
| Android | API 23 | API 34 / 16* | Chrome Custom Tabs with dynamic theming. |
| React Native | 0.70.0 | Latest | Nitro Modules + TypeScript bindings. |
* iOS 26 and Android 16 features are automatically gated behind runtime checks.
Expo is not supported because Nitro modules require native compilation.
npm install react-native-inappbrowser-nitro react-native-nitro-modulesor
yarn add react-native-inappbrowser-nitro react-native-nitro-modulescd ios
pod installNo additional steps—Gradle autolinking handles everything.
import { InAppBrowser } from 'react-native-inappbrowser-nitro'
async function openDocs() {
if (!(await InAppBrowser.isAvailable())) {
console.warn('No compatible browser found')
return
}
const result = await InAppBrowser.open('https://nitro.margelo.com', {
preferredBarTintColor: { base: '#111827', light: '#1F2933', highContrast: '#000000' },
preferredControlTintColor: { base: '#F9FAFB', highContrast: '#FFD700' }, // iOS 26+
toolbarColor: { base: '#2563EB', dark: '#1E3A8A' },
enablePartialCustomTab: true, // Android 16+
})
console.log(result)
}import { useInAppBrowser } from 'react-native-inappbrowser-nitro'
export function LaunchButton() {
const { open, isLoading, error } = useInAppBrowser()
return (
<Button
title={isLoading ? 'Opening…' : 'Open Browser'}
onPress={() => open('https://github.com/mCodex/react-native-inappbrowser-nitro')}
disabled={isLoading}
color={error ? 'crimson' : undefined}
/>
)
}const result = await InAppBrowser.openAuth(
'https://provider.com/oauth/authorize?client_id=abc',
'myapp://oauth/callback',
{
ephemeralWebSession: true,
enableEdgeDismiss: false, // iOS 26+: lock swipe-to-dismiss during auth
showTitle: false,
}
)
if (result.type === 'success' && result.url) {
// handle redirect
}Migrating from earlier react-native-inappbrowser-nitro versions? Note these key changes when adopting the Nitro rewrite:
Older releases resolved the promise when the browser closed. The new implementation resolves as soon as Safari/Custom Tabs is shown (mirroring Android behavior), and dismissal status is delivered asynchronously.
-const result = await InAppBrowser.open(url, options)
-// resolved after the tab closes
-console.log(result.type)
+const result = await InAppBrowser.open(url, options)
+console.log('Browser visible', result.type)
+// move teardown logic to listeners triggered on dismissIf you depended on awaiting dismissal, refactor to handle completion via deep links, event listeners, or the returned result object.
- Color props accept
DynamicColorobjects to describe light/dark/high-contrast palettes. - Added platform guards such as
enableEdgeDismiss(iOS 26),enablePartialCustomTab,includeReferrer, andshareState. - TypeScript enums/constants are exported from the root for stronger typing.
-InAppBrowser.open(url, { toolbarColor: '#6200EE' })
+InAppBrowser.open(url, { toolbarColor: { base: '#6200EE', dark: '#3700B3' } })useInAppBrowser wraps the imperative API with loading state and error capture, aligning with the new async semantics.
-const handleOpen = () => InAppBrowser.open(url)
+const { open, isLoading, error } = useInAppBrowser()
+const handleOpen = () => open(url)Run yarn codegen && yarn build (or your project script) to regenerate Nitro bindings so the new enums and options are available on both platforms.
| Method | Description |
|---|---|
isAvailable() |
Resolve to true when a compliant custom tab / Safari controller is available. |
open(url, options?) |
Launch an in-app browser session with rich customization. |
openAuth(url, redirectUrl, options?) |
Open an OAuth/SSO flow that resolves once the redirect URL is reached. |
close() |
Programmatically dismiss an open browser session. |
closeAuth() |
Abort an authentication session. |
All functions return Promises and are fully typed. See src/core/InAppBrowser.ts for the higher-level wrapper implementation.
| Option | Type | Notes |
|---|---|---|
headers |
Record<string, string> |
Additional request headers applied to the first navigation. |
| Option | Type | Default | Notes |
|---|---|---|---|
dismissButtonStyle |
'done' | 'close' | 'cancel' |
'done' |
Choose a Safari dismiss button style. |
preferredBarTintColor |
DynamicColor |
System | Navigation bar background; honors highContrast on iOS 26+. |
preferredControlTintColor |
DynamicColor |
System | Bar button tint; supports highContrast on iOS 26+. |
preferredStatusBarStyle |
'default' | 'lightContent' | 'darkContent' |
System | Override status bar text color when available. |
readerMode |
boolean |
false |
Attempt Safari Reader mode. |
animated |
boolean |
true |
Toggle presentation animations. |
modalPresentationStyle |
ModalPresentationStyle |
'automatic' |
UIKit modal presentation style. |
modalTransitionStyle |
ModalTransitionStyle |
'coverVertical' |
Use 'partialCurl' only with 'fullScreen'. |
modalEnabled |
boolean |
true |
Present modally instead of pushing. |
enableBarCollapsing |
boolean |
false |
Allow Safari toolbar to collapse on scroll. |
ephemeralWebSession |
boolean |
false |
Use ASWebAuthenticationSession ephemeral mode. |
enableEdgeDismiss |
boolean |
true |
iOS 26+: enable or block interactive swipe-to-dismiss. |
overrideUserInterfaceStyle |
'unspecified' | 'light' | 'dark' |
'unspecified' |
Force Safari appearance regardless of system theme. |
formSheetPreferredContentSize |
{ width: number; height: number } |
UIKit | Preferred size for form-sheet presentations (iPad). |
| Option | Type | Default | Notes |
|---|---|---|---|
showTitle |
boolean |
true |
Toggle title text. |
toolbarColor |
DynamicColor |
Browser default | Supports per-scheme colors & highContrast (Android 16+). |
secondaryToolbarColor |
DynamicColor |
Browser default | Secondary toolbar background. |
navigationBarColor |
DynamicColor |
OS default | API 27+. |
navigationBarDividerColor |
DynamicColor |
OS default | API 28+. |
enableUrlBarHiding |
boolean |
false |
Hide URL bar on scroll. |
enableDefaultShare |
boolean |
false |
Deprecated alias for shareState. |
shareState |
'default' | 'on' | 'off' |
'default' |
Control the share sheet button. |
colorScheme |
'system' | 'light' | 'dark' |
'system' |
Hint to Custom Tabs theme. |
headers |
Record<string, string> |
{} |
Additional request headers. |
forceCloseOnRedirection |
boolean |
false |
Close once the URL changes. |
hasBackButton |
boolean |
false |
Replace close X with a back arrow. |
browserPackage |
string |
Auto | Force a Custom Tabs provider package. |
showInRecents |
boolean |
true |
Hide or show the tab in Android recents. |
includeReferrer |
boolean |
false |
Attach Intent.EXTRA_REFERRER; may be ignored on emulators. |
instantAppsEnabled |
boolean |
true |
Allow Instant Apps fallback. |
enablePullToRefresh |
boolean |
false |
Enable built-in swipe-to-refresh. |
enablePartialCustomTab |
boolean |
false |
Android 16+: open in resizable bottom-sheet style tab. |
animations |
{ startEnter?: string; startExit?: string; endEnter?: string; endExit?: string } |
System | Animation resource names from your app. |
Provide adaptive color schemes without branching per platform by supplying a DynamicColor object:
type DynamicColor = {
base?: string
light?: string
dark?: string
highContrast?: string // iOS 26 & Android 16 accessibility override
}When highContrast is given, the native layer only applies it when the OS signals increased contrast; earlier versions gracefully fall back to base/light/dark.
- Auth hardening: combine
ephemeralWebSessionwithenableEdgeDismiss: falseto keep OAuth flows contained on iOS 26+. - Referrer propagation:
includeReferrerimproves attribution on Android but some emulator images lack Play Services and ignore it. - Partial tabs: on emulators without gesture navigation the resize handle might be hidden; use the hardware back button to exit.
- Device parity: verify flows on real hardware—emulators often skip hardened browser checks present on production devices.
- Install Chrome or another Custom Tabs capable browser via the Play Store.
- If none is available, the library falls back to the system chooser and surfaces the underlying error.
- Safari View Controller has reduced capabilities (no Apple Pay, limited Reader mode).
- Use a device to validate privacy indicators, dynamic color, and high-contrast modes.
Pull requests and bug reports are welcome! Please read our contributing guide and run yarn codegen && yarn lint before submitting changes.
MIT © mCodex
Built with ❤️ using Nitro Modules for the React Native community.
