npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-inappbrowser-nitro

v3.0.1

Published

react-native-inappbrowser-nitro is a react native package built with Nitro

Readme

react-native-inappbrowser-nitro

A modern, native in-app browser for React Native — built on Nitro Modules.

npm version npm downloads bundle size license CI CI

Installation · Quick start · API · Options · Migration · FAQ


Why this library?

| | | |---|---| | ⚡ Native speed | Direct JSI bindings via Nitro Modules — no JSON bridge, no scheduler hops. | | 🎯 Right primitive on each platform | SFSafariViewController on iOS, Chrome Custom Tabs on Android — not a WKWebView reimplementation. | | 🔐 OAuth-ready | First-class openAuth flow with ephemeral sessions and redirect interception. | | 🪝 Hook + imperative APIs | useInAppBrowser() for component state, named exports for everything else. | | 🧩 Strict TypeScript | Discriminated result types, dual as const + literal-type enums, full JSDoc with examples. | | 📦 Small footprint | "sideEffects": false, ESM-first build, lazy-initialized native module. |


Requirements

| | Minimum | Tested up to | |---|---|---| | React Native | 0.75 (New Architecture) | 0.85 | | iOS | 15.1 | 26.2 | | Android | API 23 (Android 6) | API 36 (Android 16) | | react-native-nitro-modules | 0.35 | 0.35.4 |

[!IMPORTANT] This library requires the React Native New Architecture and is not compatible with Expo Go. It works in Expo prebuild / dev clients.


Installation

yarn add react-native-inappbrowser-nitro react-native-nitro-modules
npm install react-native-inappbrowser-nitro react-native-nitro-modules
pnpm add react-native-inappbrowser-nitro react-native-nitro-modules
bun add react-native-inappbrowser-nitro react-native-nitro-modules

iOS

cd ios && pod install

Android

Autolinking handles everything. No manual MainApplication edits required.


Quick start

Imperative API

import { isAvailable, open } from 'react-native-inappbrowser-nitro'

if (await isAvailable()) {
  const result = await open('https://github.com', {
    preferredBarTintColor:     { light: '#FFFFFF', dark: '#000000' }, // iOS
    toolbarColor:              { light: '#FFFFFF', dark: '#000000' }, // Android
    readerMode: true,
  })

  if (result.type === 'success') {
    console.log('Opened', result.url)
  }
}

React hook

import { useInAppBrowser } from 'react-native-inappbrowser-nitro/hooks'

function DocsButton() {
  const { open, isLoading, error } = useInAppBrowser()

  return (
    <Pressable
      disabled={isLoading}
      onPress={() => open('https://nitro.margelo.com')}
    >
      <Text>{isLoading ? 'Opening…' : 'Open docs'}</Text>
      {error && <Text style={{ color: 'red' }}>{error.message}</Text>}
    </Pressable>
  )
}

The hook handles isLoading / error state, is safe to call after unmount (state updates are guarded), and returns stable open/openAuth references via useCallback so it's safe to put them in effect dependency arrays.

OAuth / SSO with openAuth

import { openAuth } from 'react-native-inappbrowser-nitro'

const result = await openAuth(
  'https://example.com/oauth/authorize?client_id=…&redirect_uri=myapp%3A%2F%2Fcb',
  'myapp://cb',
  {
    ephemeralWebSession: true,   // iOS: don't share cookies with Safari
    enableEdgeDismiss: false,    // iOS: disable swipe-to-dismiss while authing
    forceCloseOnRedirection: true, // Android: close tab once redirect is hit
  }
)

if (result.type === 'success' && result.url) {
  const code = new URL(result.url).searchParams.get('code')
  // …exchange the code for a token
}

API

All exports come from the package root unless noted. Every function returns a Promise.

| Export | Signature | Description | |---|---|---| | isAvailable | () => Promise<boolean> | true when a compliant Safari/Custom Tabs runtime is reachable. Always true on iOS; on Android requires a Custom Tabs–capable browser. | | open | (url, options?) => Promise<InAppBrowserResult> | Present an in-app browser. Resolves when the user dismisses or the system closes it. | | openAuth | (url, redirectUrl, options?) => Promise<InAppBrowserAuthResult> | Run an authentication session that resolves the moment the native runtime intercepts a navigation matching redirectUrl. | | close | () => Promise<void> | Dismiss the current browser. No-op when none is presented. | | closeAuth | () => Promise<void> | Cancel an in-flight openAuth session. | | useInAppBrowser | () => UseInAppBrowserReturn | Hook wrapping open/openAuth with isLoading + error state. Exported from react-native-inappbrowser-nitro/hooks. |

Result shape

type BrowserResultType = 'cancel' | 'dismiss' | 'success'

interface InAppBrowserResult {
  type: BrowserResultType
  url?: string      // final URL captured by the browser session
  message?: string  // human-readable reason on `dismiss`
}

Errors

open and openAuth reject with an Error when:

  • the URL is empty, missing a scheme, or
  • the URL uses a denied scheme (javascript:, data:, vbscript:).

These are sanity checks performed in JS before the call ever crosses JSI.


Options

open and openAuth accept a single options object that aggregates every iOS and Android knob. Cross-platform fields apply everywhere; @platform fields are silently ignored on the other platform.

iOS

| Option | Type | Default | Notes | |---|---|---|---| | dismissButtonStyle | 'done' \| 'close' \| 'cancel' | 'done' | Toolbar dismiss button label. | | preferredBarTintColor | DynamicColor | system | Toolbar background. iOS 26 limitation: see iOS 26 Liquid Glass. | | preferredControlTintColor | DynamicColor | system | Toolbar button tint. iOS 26 limitation: see below. | | preferredStatusBarStyle | 'default' \| 'lightContent' \| 'darkContent' | system | Status bar appearance while presented. | | readerMode | boolean | false | Open in Safari Reader Mode if the page supports it. | | animated | boolean | true | Animate present/dismiss. | | modalPresentationStyle | ModalPresentationStyle | 'automatic' | UIKit modal style. | | modalTransitionStyle | ModalTransitionStyle | 'coverVertical' | UIKit transition (use 'partialCurl' only with 'fullScreen'). | | modalEnabled | boolean | true | Present modally vs. push onto navigation stack. | | enableBarCollapsing | boolean | false | Collapse toolbar on scroll. | | ephemeralWebSession | boolean | false | openAuth only: don't persist cookies/credentials. | | enableEdgeDismiss | boolean | true | Allow swipe-from-edge to dismiss. | | overrideUserInterfaceStyle | 'unspecified' \| 'light' \| 'dark' | 'unspecified' | Force light/dark regardless of system theme. | | formSheetPreferredContentSize | { width, height } | UIKit | Size when modalPresentationStyle: 'formSheet' (iPad). |

Android

| Option | Type | Default | Notes | |---|---|---|---| | showTitle | boolean | false | Show page title beneath URL bar. | | toolbarColor | DynamicColor | browser default | Top toolbar background. | | secondaryToolbarColor | DynamicColor | browser default | Bottom toolbar background. | | navigationBarColor | DynamicColor | system | API 27+. | | navigationBarDividerColor | DynamicColor | system | API 28+. | | enableUrlBarHiding | boolean | false | Hide URL bar on scroll. | | enableDefaultShare | boolean | false | Show share menu item. Use shareState for finer control. | | shareState | 'default' \| 'on' \| 'off' | 'default' | Override share menu visibility. | | colorScheme | 'system' \| 'light' \| 'dark' | 'system' | Custom Tab theme hint. | | headers | Record<string, string> | {} | HTTP headers on the initial request. | | forceCloseOnRedirection | boolean | false | Auto-close tab when redirect URL matches (auth flows). | | hasBackButton | boolean | false | Show back arrow instead of "X". | | browserPackage | string | auto | Pin to a specific browser (e.g. 'com.android.chrome'). | | showInRecents | boolean | true | Keep tab in Android Recents after closing. | | includeReferrer | boolean | false | Send host app's package as Referrer. | | instantAppsEnabled | boolean | true | Allow Instant Apps to handle the URL. | | enablePullToRefresh | boolean | false | Enable swipe-to-refresh. | | enablePartialCustomTab | boolean | false | Show as resizable bottom-sheet (Android 13+). | | animations | BrowserAnimations | system | Custom enter/exit animation resource names. |

Dynamic colors

Color options accept a DynamicColor object that adapts to system appearance:

interface DynamicColor {
  base?: string         // fallback for any mode
  light?: string        // light mode override
  dark?: string         // dark mode override
  highContrast?: string // applied when "Increase Contrast" is enabled (iOS 26+, Android 16+)
}

Each value is a #RRGGBB or #AARRGGBB hex string. If a mode-specific value is missing, the platform falls back to base, then to the system default.


Platform notes

iOS 26 Liquid Glass

iOS 26 redesigned SFSafariViewController around the system Liquid Glass material. The toolbar is now a translucent surface that samples content beneath it, so:

  • preferredBarTintColor has little to no visible effect on iOS 26.
  • preferredControlTintColor is partially overridden by the system's adaptive monochrome treatment — custom tints may render with lower contrast.

This is a platform behavior change that affects every wrapper around SFSafariViewController. There is no public API to opt out of the glass material. The properties are still forwarded for iOS ≤ 18 compatibility.

If pixel-exact branding of the chrome is critical, consider a WKWebView-based component for non-auth flows. Do not use WKWebView for OAuth — it does not share Safari's process isolation, cookies, or autofill, and many providers explicitly forbid it.

Android browser fallback

Android prefers Chrome Custom Tabs when available. On devices without a Custom Tabs–capable browser the system surfaces a chooser via Intent.ACTION_VIEW, and option fields like toolbarColor are silently ignored.


Migrating from react-native-inappbrowser-reborn

- import InAppBrowser from 'react-native-inappbrowser-reborn'
+ import { open, openAuth, close, isAvailable } from 'react-native-inappbrowser-nitro'

- await InAppBrowser.open(url, { toolbarColor: '#6200EE' })
+ await open(url, { toolbarColor: { base: '#6200EE', dark: '#3700B3' } })

- await InAppBrowser.openAuth(url, redirectUrl, { ephemeralWebSession: true })
+ await openAuth(url, redirectUrl, { ephemeralWebSession: true })

Key differences:

  • Named exports instead of a default class — better tree-shaking.
  • DynamicColor objects instead of single hex strings, so the same call adapts to light/dark/high-contrast.
  • String-literal enums ('success', 'cancel', 'dismiss') for result.type — fully type-narrowable.
  • Hook-based state: useInAppBrowser() exposes isLoading and error without manual wrappers.
  • No bridge: every call goes through Nitro's JSI hybrid object.

FAQ

SFSafariViewController and Chrome Custom Tabs share the system Safari/Chrome session — including cookies, autofill, content blockers, and (critically) password autofill from iCloud Keychain / Google Password Manager. They also run in a separate process from your app, so the host app cannot read page content. This is exactly what most OAuth providers require. A WKWebView cannot offer any of that.

Yes — in Expo prebuild / dev client projects. It does not work in Expo Go (managed workflow) because Nitro requires native compilation.

No. Nitro Modules require the New Architecture (newArchEnabled=true on Android, Fabric/TurboModule autolinking on iOS).

The default Android emulator image often ships without a Custom Tabs–capable browser. Install Chrome from the Play Store image, or use a Pixel system image with Play Services preinstalled.

You're probably calling open instead of openAuth. openAuth uses ASWebAuthenticationSession, which is the only iOS API allowed to intercept a redirect URL programmatically. open uses SFSafariViewController, which can't do that.

Most often this means the URL was rejected by the JS-side validator (empty / missing scheme / denied scheme). Check result.message for the reason. Logs from the native side are also visible in Xcode / Logcat.


Contributing

Contributions are very welcome. The library is small and well-tested — a great place to land your first React Native PR.

git clone https://github.com/mCodex/react-native-inappbrowser-nitro
cd react-native-inappbrowser-nitro
yarn install
yarn codegen     # regenerate Nitro bindings + build
yarn typecheck
yarn lint

Run the example app:

cd example
yarn ios       # or: yarn android

A pre-commit hook (Husky + lint-staged + Biome) auto-formats staged files. CI runs on iOS (macos-26, Xcode 26.2) and Android (ubuntu-latest, JDK 21).


Acknowledgements


License

MIT © Mateus Andrade