# React Native Firebase

A well-tested feature-rich modular Firebase implementation for React Native. Supports both iOS & Android platforms for all Firebase services.

## Docs

### React Native Firebase

Source: https://rnfirebase.io/

```mdx

> React Native Firebase has begun to deprecate the namespaced API (i.e firebase-js-sdk `< v9` chaining API). React Native Firebase will be moving to the modular API (i.e. firebase-js-sdk `>= v9`) in the next major release. See [migration guide](/migrating-to-v22) for more information.

React Native Firebase is the officially recommended collection of packages that brings React Native support for all Firebase services on both Android and iOS apps.

React Native Firebase fully supports React Native apps built using [React Native CLI](https://reactnative.dev/docs/environment-setup?guide=native) or using [Expo](https://docs.expo.dev/).

**Source:** [github.com/invertase/react-native-firebase](https://github.com/invertase/react-native-firebase) &middot; **Packages:** [npmjs.com/org/react-native-firebase](https://www.npmjs.com/org/react-native-firebase) (`@react-native-firebase/app`, `@react-native-firebase/auth`, `@react-native-firebase/firestore`, and one package per Firebase service)

## React Native Firebase vs firebase-js-sdk

Use **React Native Firebase** (this project) when your app runs on Android and/or iOS: it wraps the native Firebase Android and iOS SDKs directly, so you get capabilities the web SDK cannot provide on mobile, like native app analytics, background/killed-state push notifications, and native Crashlytics crash reports.

Use the plain **[firebase-js-sdk](https://github.com/firebase/firebase-js-sdk)** when you're targeting web, React Native Web, or another JavaScript-only environment with no native module support. React Native Firebase already falls back to firebase-js-sdk on those platforms; see [Other / Web](#other--web) below.

## Prerequisites

Before getting started, the documentation assumes you are able to create a project with React Native and that you have an active Firebase project.
If you do not meet these prerequisites, follow the links below:

- [React Native - Setting up the development environment](https://reactnative.dev/docs/environment-setup)
- [Create a new Firebase project](https://console.firebase.google.com/)

Additionally, current versions of firebase-ios-sdk have a minimum Xcode requirement of 26.2 and the Swift 6.2.3+ toolchain, which implies a minimum macOS version of 15 (Xcode 26.2 is supported on macOS 15). See Apple's [Upcoming SDK minimum requirements](https://developer.apple.com/news/?id=ueeok6yw) for details.

## Installation for Expo projects

Integration with Expo is possible when using a [development build](https://docs.expo.dev/workflow/overview/#development-builds). You can configure the project via [config plugins](https://docs.expo.dev/config-plugins/introduction/) or manually configure the native projects yourself (the "bare workflow").

_NOTE:_ React Native Firebase cannot be used in the pre-compiled [Expo Go app](https://docs.expo.dev/workflow/overview/#expo-go-an-optional-tool-for-learning) because React Native Firebase uses native code that is not compiled into Expo Go.

> **Warning:** If you are using `expo-dev-client`, native crashes (such as those triggered by `crash(getCrashlytics())`) will **not** be reported to Firebase Crashlytics during development. This is because `expo-dev-client` provides a custom error overlay that catches and displays errors before they are sent to Firebase. To test native crash reporting, you must remove `expo-dev-client` and run your app in a standard release or debug build without the custom error overlay.

To create a new Expo project, see the [Get started](https://docs.expo.dev/get-started/create-a-project/) guide in Expo documentation.

### Install React Native Firebase modules

To install React Native Firebase's base `app` module, use the command `npx expo install @react-native-firebase/app`.

Similarly you can install other React Native Firebase modules such as for Authentication and Crashlytics: `npx expo install @react-native-firebase/auth @react-native-firebase/crashlytics`.

### Configure React Native Firebase modules

The recommended approach to configure React Native Firebase is to use [Expo Config Plugins](https://docs.expo.dev/config-plugins/introduction/). You will add React Native Firebase modules to the [`plugins`](https://docs.expo.io/versions/latest/config/app/#plugins) array of your `app.json` or `app.config.js`. See the note below to determine which modules require Config Plugin configurations.

If you are instead manually adjusting your Android and iOS projects (this is not recommended), follow the same instructions as [React Native CLI projects](/#installation-for-react-native-cli-non-expo-projects).

To enable Firebase on the native Android and iOS platforms, create and download Service Account files for each platform from your Firebase project. Then provide paths to the downloaded `google-services.json` and `GoogleService-Info.plist` files in the following `app.json` fields: [`expo.android.googleServicesFile`](https://docs.expo.io/versions/latest/config/app/#googleservicesfile-1) and [`expo.ios.googleServicesFile`](https://docs.expo.io/versions/latest/config/app/#googleservicesfile). See the example configuration below.

For iOS, React Native Firebase resolves the Firebase Apple SDK with [Swift Package Manager by default](/ios-spm) on React Native 0.75+. SPM requires dynamic frameworks — configure [`expo-build-properties`](https://docs.expo.dev/versions/latest/sdk/build-properties/#pluginconfigtypeios) with `"useFrameworks": "dynamic"`. See the example configuration below.

The following is an example `app.json` to enable the React Native Firebase modules App, Auth and Crashlytics, that specifies the Service Account files for both mobile platforms, and that sets the application ID to the example value of `com.mycorp.myapp` (change to match your own).

You will need to add a plugin entry for each react-native-firebase module you use that defines an Expo config plugin - these are documented on the specific module install pages.

```json
{
  "expo": {
    "android": {
      "googleServicesFile": "./google-services.json",
      "package": "com.mycorp.myapp"
    },
    "ios": {
      "googleServicesFile": "./GoogleService-Info.plist",
      "bundleIdentifier": "com.mycorp.myapp"
    },
    "plugins": [
      "@react-native-firebase/app",
      "@react-native-firebase/auth",
      "@react-native-firebase/crashlytics",
      [
        "expo-build-properties",
        {
          "ios": {
            "useFrameworks": "dynamic"
          }
        }
      ]
    ]
  }
}
```

> Listing a module in the Config Plugins (the `"plugins"` array in the JSON above) is only required for React Native Firebase modules that involve _native installation steps_ - e.g. modifying the Xcode project, `Podfile`, `build.gradle`, `AndroidManifest.xml` etc. React Native Firebase modules without native steps will work out of the box; no `"plugins"` entry is required. Not all modules have Expo Config Plugins provided yet. A React Native Firebase module has Config Plugin support if it contains an `app.plugin.js` file in its package directory (e.g.`node_modules/@react-native-firebase/app/app.plugin.js`).

If you use `@react-native-firebase/analytics` with Expo, including EAS Build, and want to configure iOS Analytics Podfile flags, add the Analytics config plugin with the relevant iOS options:

```json
[
  "@react-native-firebase/analytics",
  {
    "ios": {
      "withoutAdIdSupport": true,
      "googleAppMeasurementOnDeviceConversion": true
    }
  }
]
```

The `withoutAdIdSupport` option adds `$RNFirebaseAnalyticsWithoutAdIdSupport = true` to opt out of iOS Ad ID support. The `googleAppMeasurementOnDeviceConversion` option adds `$RNFirebaseAnalyticsGoogleAppMeasurementOnDeviceConversion = true` to include Google Analytics on-device conversion measurement support. You may omit either option if it is not needed.

#### CocoaPods / static frameworks (opt-out)

Do **not** combine RNFB's default SPM mode with static frameworks. To use CocoaPods instead of SPM (for example when you need static linkage, or to share one `FirebaseCore` with another native pod), pass `disableSPM: true` to the `@react-native-firebase/app` config plugin and set `"useFrameworks": "static"`. With React Native 0.84+ / Expo 54+ prebuilt core, list every RNFB native module you use in `forceStaticLinking`. See [iOS SPM Support](/ios-spm) for details:

```json
{
  "expo": {
    "plugins": [
      [
        "@react-native-firebase/app",
        {
          "ios": {
            "disableSPM": true
          }
        }
      ],
      "@react-native-firebase/auth",
      "@react-native-firebase/crashlytics",
      [
        "expo-build-properties",
        {
          "ios": {
            "useFrameworks": "static",
            "forceStaticLinking": [
              "RNFBApp",
              "RNFBAuth",
              "RNFBCrashlytics",
              "RNFBSomeOtherRNFBModuleYouAreUsing"
            ]
          }
        }
      ]
    ]
  }
}
```

### Local app compilation

If you are compiling your app locally, run [`npx expo prebuild --clean`](https://docs.expo.dev/workflow/continuous-native-generation/) to generate the native project directories. Then, follow the local app compilation steps described in [Local app development](https://docs.expo.dev/guides/local-app-development/) guide in Expo docs. If you prefer using a build service, refer to [EAS Build](https://docs.expo.dev/build/setup/).

Note: if you have already installed an Expo development build (using something like `npx expo run` after doing the `--prebuild` local development steps...) before installing react-native-firebase, then you must uninstall it first as it will not contain the react-native-firebase native modules and you will get errors with `RNFBAppModule not found` etc. If so, uninstall the previous development build, do a clean build using `npx expo prebuild --clean`, and then attempt `npx expo run:<platform>` again.

### Expo Tools for VSCode

If you are using the [Expo Tools](https://marketplace.visualstudio.com/items?itemName=expo.vscode-expo-tools) VSCode extension, the IntelliSense will display a list of available plugins when editing the `plugins` section of `app.json`.

---

## Installation for React Native CLI (non-Expo) projects

Installing React Native Firebase to a RN CLI project requires a few steps; installing the NPM module, adding the Firebase config files &
rebuilding your application.

### 1. Install via NPM

Install the React Native Firebase "app" module to the root of your React Native project with NPM or Yarn:

```bash
# Using npm
npm install --save @react-native-firebase/app

# Using Yarn
yarn add @react-native-firebase/app
```

The `@react-native-firebase/app` module must be installed before using any other Firebase service.

### 2. React Native CLI - Android Setup

To allow the Android app to securely connect to your Firebase project, a configuration file must be downloaded and added
to your project.

#### Generating Android credentials

On the Firebase console, add a new Android application and enter your projects details. The "Android package name" must match your
local projects package name which can be found inside of the `namespace` field in `/android/app/build.gradle`, or in the
`manifest` tag within the `/android/app/src/main/AndroidManifest.xml` file within your project for projects using android gradle plugin v7 and below

> The debug signing certificate is optional to use Firebase with your app, but is required for Invites and Phone Authentication.
> To generate a certificate run `cd android && ./gradlew signingReport`. This generates two variant keys.
> You have to copy **both** 'SHA1' and 'SHA-256' keys that belong to the 'debugAndroidTest' variant key option.
> Then, you can add those keys to the 'SHA certificate fingerprints' on your app in Firebase console.

Download the `google-services.json` file and place it inside of your project at the following location: `/android/app/google-services.json`.

#### Configure Firebase with Android credentials

To allow Firebase on Android to use the credentials, the `google-services` plugin must be enabled on the project. This requires modification to two
files in the Android directory.

First, add the `google-services` plugin as a dependency inside of your `/android/build.gradle` file:

```groovy
buildscript {
  dependencies {
    // ... other dependencies
    classpath 'com.google.gms:google-services:4.5.0'
    // Add me --- /\
  }
}
```

Lastly, execute the plugin by adding the following to your `/android/app/build.gradle` file:

```groovy
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services' // <- Add this line
```

### 3. React Native CLI - iOS Setup

To allow the iOS app to securely connect to your Firebase project, a configuration file must be downloaded and added to your project, and you must enable frameworks in CocoaPods

#### Generating iOS credentials

On the Firebase console, add a new iOS application and enter your projects details. The "iOS bundle ID" must match your
local project bundle ID. The bundle ID can be found within the "General" tab when opening the project with Xcode.

Download the `GoogleService-Info.plist` file.

Using Xcode, open the projects `/ios/{projectName}.xcodeproj` file (or `/ios/{projectName}.xcworkspace` if using Pods).

Right click on the project name and "Add files" to the project, as demonstrated below:

![Add files via Xcode](https://images.prismic.io/invertase/717983c0-63ca-4b6b-adc5-31318422ab47_add-files-via-xcode.png?auto=format)

Select the downloaded `GoogleService-Info.plist` file from your computer, and ensure the "Copy items if needed" checkbox is enabled.

![Select 'Copy Items if needed'](https://prismic-io.s3.amazonaws.com/invertase%2F7d37e0ce-3e79-468d-930c-b7dc7bc2e291_unknown+%282%29.png)

#### Configure Firebase with iOS credentials (react-native 0.77+)

To allow Firebase on iOS to use the credentials, the Firebase iOS SDK must be configured during the bootstrap phase of your application.

To do this, open your `/ios/{projectName}/AppDelegate.swift` file and add the following:

At the top of the file, import the Firebase SDK right after `'import ReactAppDependencyProvider'`:

```swift
import Firebase
```

Within your existing `application` method, add the following to the top of the method:

```swift
  override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
  // Add me --- \/
  FirebaseApp.configure()
  // Add me --- /\
  // ...
}
```

#### Configure Firebase with iOS credentials (react-native < 0.77)

To allow Firebase on iOS to use the credentials, the Firebase iOS SDK must be configured during the bootstrap phase of your application.

To do this, open your `/ios/{projectName}/AppDelegate.mm` file (or `AppDelegate.m` if on older react-native), and add the following:

At the top of the file, import the Firebase SDK right after `'#import "AppDelegate.h"'`:

```objectivec
#import <Firebase.h>
```

Within your existing `didFinishLaunchingWithOptions` method, add the following to the top of the method:

```objectivec
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  // Add me --- \/
  [FIRApp configure];
  // Add me --- /\
  // ...
}
```

#### Altering CocoaPods to use frameworks

Beginning with firebase-ios-sdk v9+ (react-native-firebase v15+) you must tell CocoaPods to use frameworks. On React Native 0.75+, React Native Firebase resolves the Firebase Apple SDK with [Swift Package Manager by default](/ios-spm), which **requires dynamic linkage**.

Open the file `./ios/Podfile` and add this line inside your targets (right before the `use_react_native` line in current react-native releases that calls the react native Podfile function to get the native modules config):

```ruby
use_frameworks! :linkage => :dynamic
```

Do **not** combine RNFB's default SPM mode with `use_frameworks! :linkage => :static` — `pod install` rejects that combination. Full requirements, Expo notes, and troubleshooting are in [iOS SPM Support](/ios-spm).

##### CocoaPods / static frameworks (opt-out)

To opt out of SPM and use CocoaPods for Firebase (for example when you need static linkage, or to share one `FirebaseCore` with another native pod), set `$RNFirebaseDisableSPM = true` **before** any target block, then configure static frameworks:

```ruby
# before any target block
$RNFirebaseDisableSPM = true

# inside your app target, before use_react_native!
use_frameworks! :linkage => :static
$RNFirebaseAsStaticFramework = true
```

> **Note:** `use_frameworks` is [not compatible with Flipper](https://github.com/reactwg/react-native-releases/discussions/21#discussioncomment-2924919). Flipper is deprecated in the React Native community; if your Podfile still references `:flipper_configuration`, remove or comment it out.

### 4. Autolinking & rebuilding

Once the above steps have been completed, the React Native Firebase library must be linked to your project and your application needs to be rebuilt.

Users on React Native 0.60+ automatically have access to "[autolinking](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md)",
requiring no further manual installation steps. To automatically link the package, rebuild your project:

```bash
# Android apps
npx react-native run-android

# iOS apps
cd ios/
pod install --repo-update
cd ..
npx react-native run-ios
```

Once successfully linked and rebuilt, your application will be connected to Firebase using the `@react-native-firebase/app` module. This module does not provide much functionality, therefore to use other Firebase services, each of the modules for the individual Firebase services need installing separately.

---

## Other / Web

If you are using the firebase-js-sdk fallback support for [web or "other" platforms](platforms#other-platforms) then you must initialize Firebase dynamically by calling [`initializeApp`](https://invertase.github.io/react-native-firebase/_react-native-firebase/app/initializeApp.html).

However, you only want to do this for the web platform. For non-web / native apps the "default" firebase app instance will already be configured by the native google-services.json / GoogleServices-Info.plist files as mentioned above.

At some point during your application's bootstrap processes, initialize firebase like this:

```javascript
import { getApp, initializeApp } from '@react-native-firebase/app';

// web requires dynamic initialization on web prior to using firebase
if (Platform.OS === 'web') {
  const firebaseConfig = {
    // ... config items pasted from firebase console for your web app here
  };

  initializeApp(firebaseConfig);
}

// ...now throughout your app, use firebase APIs normally, for example:
const firebaseApp = getApp();
```

---

## Miscellaneous

### Overriding Native SDK Versions

React Native Firebase internally sets the versions of the native SDKs which each module uses. Each release of the library
is tested against a fixed set of SDK versions (e.g. Firebase SDKs), allowing us to be confident that every feature the
library supports is working as expected.

Sometimes it's required to change these versions to play nicely with other React Native libraries or to work around temporary build failures; therefore we allow
manually overriding these native SDK versions.

> Using your own SDK versions is not recommended and not supported as it can lead to unexpected build failures when new react-native-firebase versions are released that expect to use new SDK versions. Proceed with caution and remove these overrides as soon as possible when no longer needed.

#### Android

Within your projects /android/build.gradle file, provide your own versions by specifying any of the following options shown below:

```groovy
project.ext {
  set('react-native', [
    versions: [
      // Overriding Build/Android SDK Versions if desired
      android : [
        minSdk    : 23,
        targetSdk : 33,
        compileSdk: 34,
      ],

      // Overriding Library SDK Versions if desired
      firebase: [
        // Override Firebase SDK Version
        bom           : "34.18.0"
      ],
    ],
  ])
}
```

Once changed, rebuild your application with `npx react-native run-android`.

#### iOS

Open your projects `/ios/Podfile` and add any of the globals shown below to the top of the file:

```ruby
# Override Firebase SDK Version if desired
$FirebaseSDKVersion = '12.18.0'
```

Once changed, reinstall your projects pods via pod install and rebuild your project with `npx react-native run-ios`.

Alternatively, if you cannot edit the Podfile easily (as when using Expo), you may add the environment variable `FIREBASE_SDK_VERSION=12.18.0` (or whatever version you need) to the command line that installs pods. For example `FIREBASE_SDK_VERSION=12.18.0 yarn expo prebuild --clean`

### Android Performance

On Android, React Native Firebase uses [thread pool executor](https://developer.android.com/reference/java/util/concurrent/ThreadPoolExecutor) to provide improved performance and managed resources.
To increase throughput, you can tune the thread pool executor via `firebase.json` file within the root of your project:

```json
// <project-root>/firebase.json
{
  "react-native": {
    "android_task_executor_maximum_pool_size": 10,
    "android_task_executor_keep_alive_seconds": 3
  }
}
```

| Key                                        | Description                                                                                                                                                                                                                                                                                     |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `android_task_executor_maximum_pool_size`  | Maximum pool size of ThreadPoolExecutor. Defaults to `1`. Larger values typically improve performance when executing large numbers of asynchronous tasks, e.g. Firestore queries. Setting this value to `0` completely disables the pooled executor and all tasks execute in serial per module. |
| `android_task_executor_keep_alive_seconds` | Keep-alive time of ThreadPoolExecutor, in seconds. Defaults to `3`. Excess threads in the pool executor will be terminated if they have been idle for more than the keep-alive time. This value doesn't have any effect when the maximum pool size is lower than `2`.                           |
```

### About

Source: https://rnfirebase.io/about

```mdx

# About React Native Firebase

The React Native Firebase project started life in late 2016 as a personal project [Mike Diarmid](https://twitter.com/mikediarmid)
and [Elliot Hesp](https://twitter.com/elliothesp) were working on. The project gained instant popularity, allowing React Native developers
to hook into the native services Firebase provides.

During the first few months of its development, the repository was moved into the organization of Invertase on GitHub.
The organization was a collection of the personal and combined Open Source efforts Mike and Elliot had been
working over the past number of years. With a constant flow of feature requests, issues and contributions,
the library rapidly grew in popularity, achieving over 100k NPM downloads in its first 12 months of development.

In mid-2017, we were approached by Google, who offered to help ensure the projects continued support by providing direct contact with the
Firebase team and by providing funding to ensure the project had dedicated time assigned to its upkeep.

In the 2018 Firebase Summit in Prague during the opening keynote, Google openly announced their
working relationship with Invertase - check it out below:

[https://twitter.com/rnfirebase/status/1056839638961348608/video/1](https://twitter.com/rnfirebase/status/1056839638961348608/video/1)

## Future of the library

The React Native Firebase library has been a huge driving force for our knowledge and experience. Even with years of
experience in Open Source, JavaScript, Android and iOS, we're constantly learning and improving the library -
whilst focusing on the needs of the community and many users of the library. Maintaining a popular Open Source
library is hard, yet rewarding work.

With the support from the community and Google, we're pleased to announce that starting in 2019, our very own
Mike Diarmid will be working full time on the library. With the lessons we've learnt, we'll be focusing on taking the
library to a new level. Read about it [here](https://medium.com/invertase/react-native-firebase-2019-7e334ca9bcc6).

The success and upkeep of the library would not be possible without the support and contributions from the community.
To date, we've had contributions from 114 members of the Open Source community on the repository to who we're very
grateful to, the project wouldn't be where it is without you. Special thanks to Chris Bianca
at [CS Frequency Limited](http://invertase.link/csf-website) for their work in helping to get this project off the ground.

We'd also like to extend our thanks to our backers on Open Collective, whose contributions have made it possible to
sustain the vast amount of time required in the upkeep of such a project.

The future of Invertase and React Native Firebase is exciting.
```

### Enabling Multidex

Source: https://rnfirebase.io/enabling-multidex

```mdx

As more native dependencies are added to your project, it may bump you over the
64k method limit on the Android build system. Once this limit has been reached, you will start to see the following error
whilst attempting to build your Android application:

```
Execution failed for task ':app:mergeDexDebug'.
```

To learn more about multidex, view the official [Android documentation](https://developer.android.com/studio/build/multidex#mdex-gradle).

## Enabling Multidex

There are 3 steps involved in enabling multidex.

Steps 1 and 2 tell Gradle to turn on multidex with a directive, and add new dependency.

Open the `/android/app/build.gradle` file. Under `dependencies` we need to add the module, and then enable it
within the `defaultConfig`:

```groovy
android {
    defaultConfig {
        // ...
        multiDexEnabled true // <-- ADD THIS in the defaultConfig section
    }
    // ...
}

dependencies {
  implementation 'androidx.multidex:multidex:2.0.1'  // <-- ADD THIS DEPENDENCY
}
```

The 3rd step is to alter your `android/app/src/main/java/.../MainApplication.java` file to extend `MultiDexApplication` like so:

```java

// ... all your other imports here
import androidx.multidex.MultiDexApplication; // <-- ADD THIS IMPORT


// Your class definition needs `extends MultiDexApplication` like below
public class MainApplication extends MultiDexApplication implements ReactApplication {

```

Once added, rebuild your application: `npx react-native run-android`.
```

### FAQs and Tips

Source: https://rnfirebase.io/faqs-and-tips

```mdx

Over the years, there’s been a lot of discussions on our [GitHub](https://github.com/invertase/react-native-firebase) and [Discord](https://invertase.link/discord). Many of them have been about common problems developers face when using our package, and some of them resulted in very good advice being given.

In order to save others time and frustration, this page has been created to document some of these common problems and good pieces of advice.
If you come across a discussion that results in great advice that can benefit many developers, or a discussion that resolves a problem that many developers encounter, please do add it here! Someone will definitely be grateful.

# FAQs

### Why `react-native-firebase` over `firebase-js-sdk`?

This package wraps `firebase-android-sdk` and `firebase-ios-sdk` into a Javascript API for React Native projects, so the main benefits come with the access to native code.

- There are more modules in the native SDKs than the web SDK because some things only make sense in a mobile / native context ( App Distribution, Crashlytics), so you can actually do more, and some of it is important for example, monitoring quality with Crashlytics

- Some of the modules that are both in the web SDK and native SDK have a great deal more functionality when they can harness native APIs, like messaging (with background delivery that can start your app if not running), like App Check where you can tie the attestation to device-level providers, Storage where you can do background downloads, Performance where you can start measurements from boot, etc.

### Does it work with New Architecture / Fabric / TurboModules ?

As far as we know, yes. We test with new architecture enabled now, and many of our users are using react-native 0.76+ with new architecture enabled.

We are not aware of any problems with the react-native-firebase modules themselves. Please let us know if you see anything.

However, we are still running in the TurboModule interoperability mode as we have not directly ported to Fabric internally yet. Migration is planned in the future but may still be a while.

### I need help with [anything regarding v5 or earlier of React Native Firebase]. Where could I get help with that?

React Native Firebase v5 is now deprecated and unsupported. There's been over a year's grace period provided to migrate to v6, so moving forward maintainers probably won't pay much attention to issues regarding v5. Understandably, upgrading to v6 can take some effort, but staying on v5 probably isn't a great choice for the long-term health of your project.
Lots of the breaking changes that were introduced were either due to upstream deprecations in the official SDKs, or to simply make the package more stable and more representative of how the actual SDKs work.
The longer you stay on v5, the more your project will be out of sync with the official SDKs, unfortunately. Couple that with the fact that it's no longer actively supported, and that's trouble looming over the horizon for your project.

We highly recommend taking the necessary pains to update to v6.

### My CI build hangs at the "Running script '[CP-User] [RNFB] Core Configuration'" step

This may be fixed by creating a `firebase.json` file at the root of your project if it's not there already. If you don't want to change any of the default React Native Firebase configurations, you can leave it empty in the following way:

```
{
  "react-native": {
  }
}
```

### I have a custom Analytics parameter called 'items' and it's not showing up on the Firebase console. How come?

This happens to be a known problem with the upstream Analytics SDKs. The Firebase team doesn't have any plans to fix it soon. More information about this can be found [here](https://github.com/invertase/react-native-firebase/issues/4018#issuecomment-682174087).

### I'm receiving `InternalFirebaseAuth.FIREBASE_AUTH_API is not available on this device`. How do I fix this?

To use some Firebase services (like auth) in an emulator, you need an Android virtual device with Google Play services installed. Check this [Stack Overflow post](https://stackoverflow.com/a/46246782/2275865) for instructions on creating a new Android virtual device with the necessary APIs installed.

### I'm getting an SIGABRT error in Xcode when faking a crash on iOS. How do I fix this?

When you get an error on this line when faking a crash on iOS:

```
RCT_EXPORT_METHOD(crash) {
  if ([RNFBCrashlyticsInitProvider isCrashlyticsCollectionEnabled]) {
    assert(NO);
  }
}
```

Just disable your debugger in Xcode. 'Project name' -> 'Edit Scheme...' -> 'Run' -> deselect "Debug executable"

### I have the latest SDK installed, but I can't send a test in app message from the console. How do I fix this?

Sometimes when building an in-app-message in the console, sending a test to a device is not possible, as the "Test on device" button is grayed out. This can be very annoying, but have found a "work around" that enables the button:

1. Make sure to fill out all fields in the first step of the form. If that doesn't enable the button:
1. Make sure to click inside every field, even if the field does not need to be updated (specially the "Text color" ones). If that doesn't enable the button:
1. Change between the "Message layout" options (Card, Modal, Image only and Top banner).

Sometimes, after step 3, you have to click inside a "Text color" field, but this should enable the "Test on device" option. After that you add the device Install ID, make sure to quit the app before the actual test, and then I wait for the confirmation toast to open the app up again. As long as the ID is 100% correct, the test should work as intended.

### On iOS, when the app is in quit state, the setBackgroundMessageHandler is never invoked even when I receive the notification. How can I fix this?

> Note: If you use @notifee/react-native, since v7.0.0, `onNotificationOpenedApp` and `getInitialNotification` will no longer trigger as notifee will handle the event.

When the app is closed/quit, this can happen even when you are getting notifications and even when you are able to invoke the app in a headless state.

To fix this:

1. You first need to send the payload with "content-available: 1" in the `apns` section of the message payload so the app gets invoked in a headless state.
2. On the `index.js` page, if the app is invoked in headless mode, instead of returning null, return a simple component that does nothing and renders nothing. Otherwise, return the actual `App` component.

To view the complete detail for this solution, please refer to this page: [#5656](https://github.com/invertase/react-native-firebase/issues/5656)

# Tips

- Whenever you face a strange issue (or an issue that causes build errors), there are two things you should always consider.
  - Build processes are costly and complex, so caching is used a lot. As a result, certain changes that you make in your app can cause cache conflicts in subsequent builds. Deal with this via `npx react-native-clean-project`. This does solve a lot of problems.
  - Try and isolate the problem with a template React Native Firebase app. This [bash script](https://github.com/mikehardy/rnfbdemo/blob/main/make-demo.sh) is particularly helpful in making an empty template app.
- Advice on supporting multiple environments (for example, dev, prod, maybe also staging, qa) for your React Native Firebase App: [#3504](https://github.com/invertase/react-native-firebase/issues/3504)
- Using [Fastlane for iOS deployment](https://docs.fastlane.tools/getting-started/ios/setup/) together with [RN Firebase Crashlytics](https://rnfirebase.io/crashlytics/usage) within CI has been observed to cause builds that hang indefinitely. Using `setup_ci(force: true)` before building the application may solve the issue.: [#3706](https://github.com/invertase/react-native-firebase/issues/3706)
- Be careful if you are using a VPN, Google blocks many VPN IPs causing "unavailable" errors for various firebase API calls, and on an Android emulator it might completely mess up the network adapter causing network calls to never return, not only on firebase but on the entire emulated phone.
```

### Android Installation

Source: https://rnfirebase.io/install-android

```mdx

# Android Manual Installation

The following steps are only required if you are using React Native 0.59 or earlier and need to manually integrate the library.

## 1. Update Gradle Settings

Add the following to your projects `/android/settings.gradle` file:

```groovy
include ':@react-native-firebase_app'
project(':@react-native-firebase_app').projectDir = new File(rootProject.projectDir, './../node_modules/@react-native-firebase/app/android')
```

## 2. Update Gradle Dependencies

Add the React Native Firebase module dependency to your `/android/app/build.gradle` file:

```groovy
dependencies {
  // ...
  implementation project(path: ":@react-native-firebase_app")
}
```

## 3. Add package to the Android Application

Import and apply the React Native Firebase module package to your `/android/app/src/main/java/**/MainApplication.java` file:

Import the package:

```java
import io.invertase.firebase.app.ReactNativeFirebaseAppPackage;
```

Add the package to the registry:

```java
protected List<ReactPackage> getPackages() {
  return Arrays.asList(
    new MainReactPackage(),
    new ReactNativeFirebaseAppPackage(),
```

## 4. Rebuild the project

Once the above steps have been completed, rebuild your Android project:

```bash
npx react-native run-android
```
```

### iOS Installation

Source: https://rnfirebase.io/install-ios

```mdx

# iOS Manual Installation

The following steps are only required if you are using React Native 0.59 or earlier, and need to manually integrate the library.

## 1. Add the Pod

Add the `RNFBApp` Pod to your projects `/ios/Podfile`:

```ruby
target 'app' do
  #  ...
  pod 'RNFBApp', :path => '../node_modules/@react-native-firebase/app'
end
```

## 2. Update Pods & rebuild the project

You may need to update your local Pods in order for the `RNFBApp` Pod to be installed in your project:

```bash
$ cd ios/
$ pod install --repo-update
```

Once the Pods have installed locally, rebuild your iOS project:

```bash
npx react-native run-ios
```
```

### iOS SPM Support

Source: https://rnfirebase.io/ios-spm

```mdx

# iOS SPM Support

React Native Firebase can resolve its Firebase Apple SDK dependencies with
[Swift Package Manager (SPM)](https://www.swift.org/package-manager/) or CocoaPods.
This changes only how the native Firebase SDK is installed; your JavaScript and
native application code do not change.

## Requirements and defaults

- React Native 0.75 or newer uses SPM by default.
- `$RNFirebaseDisableSPM = true` selects CocoaPods.
- React Native versions older than 0.75 use CocoaPods because their pod tooling
  does not provide `spm_dependency`.
- SPM requires CocoaPods 1.10 or newer. React Native 0.75+ templates already
  satisfy this requirement.

SPM requires dynamic framework linkage:

```ruby
use_frameworks! :linkage => :dynamic
```

Do not use RNFB's SPM mode with `use_frameworks! :linkage => :static`.
firebase-ios-sdk SPM products are automatic libraries (plain `.library(...)`,
not `type: .dynamic`), so each pod that depends on them embeds its own copy.
With static pod linkage those copies collide at link time as duplicate-symbol
errors. `pod install` fails fast for that combination instead of surfacing the
linker failure later. Dynamic linkage is required for SPM mode to build and
run; see [Sharing FirebaseCore with other native pods](#sharing-firebasecore-with-other-native-pods)
for what dynamic linkage does and does not give you.

CocoaPods mode supports either static or dynamic linkage, subject to the
requirements of the other dependencies in your app.

## Expo projects

Expo projects can select dynamic linkage with
[`expo-build-properties`](https://docs.expo.dev/versions/latest/sdk/build-properties/):

```json
{
  "expo": {
    "plugins": [
      [
        "expo-build-properties",
        {
          "ios": {
            "useFrameworks": "dynamic"
          }
        }
      ]
    ]
  }
}
```

To use CocoaPods mode in a generated project, pass `disableSPM` to the
`@react-native-firebase/app` config plugin -- it adds
`$RNFirebaseDisableSPM = true` before the target blocks during prebuild --
and configure the linkage your project requires:

```json
{
  "expo": {
    "plugins": [
      [
        "@react-native-firebase/app",
        {
          "ios": {
            "disableSPM": true
          }
        }
      ]
    ]
  }
}
```

The sections below (**Use SPM (React Native CLI)**, **Use CocoaPods (React
Native CLI)**) describe editing `ios/Podfile` directly. Do not follow them in
an Expo project -- Expo regenerates `ios/Podfile` from your config plugins on
every prebuild, so a direct edit does not persist. Use `expo-build-properties`
(or your own config plugin) instead, as shown above.

## Use SPM (React Native CLI)

For React Native 0.75 or newer, SPM is selected automatically. Configure dynamic
linkage in your `ios/Podfile`, then install pods normally:

```ruby
use_frameworks! :linkage => :dynamic
```

```bash
cd ios
pod install
```

The install output identifies the selected mode:

```text
[react-native-firebase] RNFBApp: Using SPM for Firebase dependency resolution (products: FirebaseCore, FirebaseInstallations)
```

RNFB also adds an `[RNFB] Embed Firebase SPM Frameworks` build phase to the app
target. This phase embeds Firebase frameworks built by SPM that React Native's
pod-level SPM integration does not otherwise copy into the app bundle.

### Release build and module-resolution settings

RNFB automatically applies two build settings needed for SPM + dynamic
linkage, on every `pod install`/`pod update`:

- `-ObjC` in the app target's linker flags, so Release's dead-code stripping
  does not drop Firebase's `FIRLibrary`/`FIRComponent` registration.
- `SWIFT_ENABLE_EXPLICIT_MODULES = 'NO'` and `CLANG_ENABLE_EXPLICIT_MODULES =
'NO'` on both the app project and the Pods project, so Xcode 26's explicit
  Swift and Clang modules don't fail to resolve Firebase's internal-only SPM
  targets (`FirebaseCoreInternal`, `FirebaseSharedSwift`) or `@import`s of
  Firebase modules in your own Objective-C/Objective-C++ files. This does not
  disable SPM -- it makes Swift and Clang use implicit module discovery
  across the app, CocoaPods, and SPM build boundary.

You do not need to configure either of these yourself. If `pod install` warns
that they could not be applied automatically, or if Xcode still reports that a
Firebase module cannot be resolved, add the fallback in your existing
`post_install` block, after `react_native_post_install`:

```ruby
post_install do |installer|
  react_native_post_install(
    installer,
    config[:reactNativePath],
    # your existing options
  )

  rnfirebase_apply_spm_build_settings(installer)
end
```

### Framework-embedding fallback

RNFB normally installs its SPM framework-embedding phase automatically after
CocoaPods has integrated `[CP] Embed Pods Frameworks` into the app target
(`post_integrate`). If `pod install` warns that automatic embedding could not
be configured, call the documented fallback from `post_integrate`:

```ruby
post_integrate do |installer|
  rnfirebase_add_spm_embed_phase(installer)
end
```

Do not add the fallback unless the warning appears or the generated app target
does not contain `[RNFB] Embed Firebase SPM Frameworks`.

## Use CocoaPods (React Native CLI)

To opt out of SPM, add this before any target block in your Podfile:

```ruby
$RNFirebaseDisableSPM = true
```

Then use the static or dynamic linkage required by your project and run
`pod install`. The install output confirms the fallback:

```text
[react-native-firebase] RNFBApp: SPM disabled ($RNFirebaseDisableSPM = true), using CocoaPods for Firebase dependencies
```

`$RNFirebaseDisableSPM` must be set to exactly `true` to opt out of SPM. Any
other value -- `false`, `nil`, or leaving it unset -- uses SPM (React Native
0.75+ default). This matters for generated or template-based Podfiles that
always emit the variable, e.g. `$RNFirebaseDisableSPM = false`: that still
uses SPM, it does not fall back to CocoaPods.

## Sharing FirebaseCore with other native pods

React Native attaches SPM products to each pod target via `spm_dependency`.
Because firebase-ios-sdk products are automatic libraries, each dynamic pod
framework that depends on Firebase embeds its own copy of `FirebaseCore`.
`FirebaseApp.configure()` in the app configures only the copy that target
links. Another pod's copy is never configured, so `FirebaseApp.app()` can
return `nil` there (and Auth can silently look signed-out even when RNFB JS
Auth has a user).

| RNFB                                       | Other native dependency | Shared FirebaseCore?                           |
| ------------------------------------------ | ----------------------- | ---------------------------------------------- |
| SPM                                        | SPM (`spm_dependency`)  | No: each dynamic pod embeds its own copy       |
| SPM                                        | CocoaPods Firebase pods | No: dual resolution (SPM + CocoaPods Firebase) |
| CocoaPods (`$RNFirebaseDisableSPM = true`) | CocoaPods Firebase pods | Yes: one CocoaPods `FirebaseCore`              |

If a third-party native module must share the same configured `FirebaseApp`
with RNFB, opt out of SPM and resolve Firebase through CocoaPods for every
native consumer:

```ruby
$RNFirebaseDisableSPM = true
```

Do not mix RNFB SPM with a third-party pod that pulls Firebase via CocoaPods
pods. That is dual resolution and does not produce one shared runtime.

Static pod linkage is not a sharing workaround under SPM: it fails at link
time with duplicate symbols (and `pod install` rejects the combination).

## Troubleshooting

- **`pod install` fails with "SPM + static linkage is not supported":** Do not
  combine RNFB SPM mode with static linkage. Use dynamic linkage
  (`use_frameworks! :linkage => :dynamic`) or set
  `$RNFirebaseDisableSPM = true`.
- **`FirebaseApp.app()` is `nil` / Auth looks signed-out inside another native
  pod, or runtime logs duplicate `FIRApp` classes:** SPM mode cannot share one
  `FirebaseCore` across dynamic pod frameworks. Set
  `$RNFirebaseDisableSPM = true` and resolve Firebase via CocoaPods for RNFB
  and that dependency. See
  [Sharing FirebaseCore with other native pods](#sharing-firebasecore-with-other-native-pods).
- **`FirebaseCoreInternal`/`FirebaseSharedSwift` cannot be resolved, or a
  Release/Archive build crashes at launch with a missing Firebase symbol:**
  RNFB applies `-ObjC`, `SWIFT_ENABLE_EXPLICIT_MODULES = 'NO'`, and
  `CLANG_ENABLE_EXPLICIT_MODULES = 'NO'` automatically; call
  `rnfirebase_apply_spm_build_settings(installer)` as a fallback only if
  `pod install` warns it could not do so.
- **A Firebase framework is missing at app launch:** Re-run `pod install`, check
  for its automatic-embedding warning, and confirm the app target contains
  `[RNFB] Embed Firebase SPM Frameworks`. Use the fallback only when needed.
- **`spm_dependency` is unavailable:** Upgrade to React Native 0.75 or newer,
  or continue with the automatic CocoaPods fallback.
- **SPM and CocoaPods both produce the same Firebase framework:** Remove manually
  declared Firebase pods/packages so RNFB owns Firebase dependency resolution.
- **Release archive fails with an `xcframework-ios.signature` "couldn't be
  copied ... because an item with the same name already exists" error:** a
  long-standing Xcode Archive bug with SPM binary xcframeworks, not an RNFB
  regression. RNFB works around it automatically; if the warning says it could
  not do so, add a Run Script build phase to your app target that removes the
  named artifact:

  ```bash
  rm -f "${CONFIGURATION_BUILD_DIR}"/<NameFromTheErrorMessage>.xcframework-ios.signature
  ```

- **Archive fails with `Undefined symbols ... _OBJC_CLASS_$_FIRApp`:** RNFB
  links `FirebaseCore` into your app target automatically (after CocoaPods
  integrates `[CP] Embed Pods Frameworks`) so the required
  `[FIRApp configure]`/`FirebaseApp.configure()` call links. This only
  surfaces if that automatic step could not run; call
  `rnfirebase_add_spm_core_to_app_target(installer)` from `post_integrate`
  as a fallback if your own native code also calls Firebase APIs directly.
- **A stale "firebase-ios-sdk" Swift Package reference remains on the app
  target after switching from SPM to `$RNFirebaseDisableSPM = true`:** RNFB
  removes it automatically on the next `pod install`; if that could not run,
  remove the "firebase-ios-sdk" Swift Package dependency from the app target
  manually in Xcode.

After changing dependency resolution, verify both a Debug build and a Release
archive. A simulator build alone does not exercise all framework-embedding and
archive-time processing behavior.

### tvOS hybrid-linkage TestFlight crash

A custom hybrid setup has reported an immediate tvOS TestFlight launch failure
when Firebase is statically linked into the app while RNFB is built as dynamic
frameworks with `-undefined dynamic_lookup`. Simulator and direct Xcode installs
may still work because App Store archive processing strips symbols differently.

This is not the standard RNFB SPM configuration. Prefer a consistent linkage
model. If the hybrid setup is unavoidable, first confirm the archived app binary
no longer exports the Firebase symbols required by the RNFB frameworks. The
reported workaround is to preserve those symbols on the main app target's
Release configuration:

```ruby
installer.aggregate_targets.each do |aggregate_target|
  aggregate_target.user_project.native_targets.each do |target|
    next unless target.name == 'YourAppTargetName' # main app target, not a Pod target

    target.build_configurations.each do |config|
      next unless config.name == 'Release'

      config.build_settings['STRIP_SWIFT_SYMBOLS'] = 'NO'
      config.build_settings['DEAD_CODE_STRIPPING'] = 'NO'
      config.build_settings['STRIP_INSTALLED_PRODUCT'] = 'NO'
      config.build_settings['COPY_PHASE_STRIP'] = 'NO'
      config.build_settings['DEPLOYMENT_POSTPROCESSING'] = 'NO'

      flags = Array(config.build_settings['OTHER_LDFLAGS'] || ['$(inherited)'])
      flags << '-Wl,-export_dynamic' unless flags.include?('-Wl,-export_dynamic')
      config.build_settings['OTHER_LDFLAGS'] = flags
    end
  end
  aggregate_target.user_project.save
end
```

This workaround increases binary size and disables Release optimizations. Apply
it only to the affected target after reproducing this specific archive-only
failure.
```

### Migrating to v6

Source: https://rnfirebase.io/migrating-to-v6

```mdx

# Introduction

This is a reference for upgrading from React Native Firebase v5.x.x to v6.x.x. Even though there is a lot to cover,
each module generally follows similar steps to migrate.

We highly recommend your project is using React Native 0.60+ before upgrading to take advantage of new features to make
the migration process much simpler.

> We highly recommend backing up your project before migrating!

If you're looking to start fresh, check out the [Getting Started](/) section of the documentation.

## Why you should migrate

React Native Firebase version 6 has been re-created from the ground up, with a heavy focus on testing, documentation & feature
compatibility with the Firebase SDKs. We've also been working closely with the Firebase team to ensure all module APIs have
been approved before being released.

We have also ensured the release is compatible with some of the popular tooling in the React Native community, such as
[autolinking](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md) & [TypeScript](https://reactnative.dev/docs/typescript).

Version 6 also brings support for previously unsupported modules such as [Firebase ML](https://firebase.google.com/docs/ml).

## NPM dependency changes

Prior to version 6, all modules are installable from the `react-native-firebase` NPM package. With version 6 we are
now taking advantage of NPM organizations, allowing us to distribute each module as its own package. This has a number
of advantages such as smaller app bundle sizes (you only install what modules you need), and internally we treat each module
as its own package, allowing for easier testing and quality assurance. Every project must install the `@react-native-firebase/app`
module, replacing the `react-native-firebase` module.

## Removing `react-native-firebase`

There are a number of steps to carry out to remove the `react-native-firebase` module from your existing app. To help make this process
easier, we'll break out the process into 3 sections:

- [Removing v5 from JavaScript](/migrating-to-v6#removing-v5-from-javascript)
- [Removing v5 from Android](/migrating-to-v6#removing-v5-from-android)
- [Removing v5 from iOS](/migrating-to-v6#removing-v5-from-ios)

---

### Removing v5 from JavaScript

As mentioned above, we need to remove the `react-native-firebase` NPM module from our project. To do this, open your projects
`package.json` file and remove the dependency:

```diff
{
  "dependencies": {
    "react": "16.8.3",
    "react-native": "0.59.9",
-   "react-native-firebase": "^5.5.4"
  }
}
```

To remove the package from your local environment, delete the `yarn.lock`/`package-lock.json` files and reinstall
the project dependencies with `yarn`.

---

### Removing v5 from Android

Removing version 5 from your native Android code is a more involved process. We'll go file by file to ensure all references
to the older version have been removed.

#### Removing from Gradle Settings

Open up your projects `/android/settings.gradle` file. There will be 2 lines which need to be removed:

```diff
rootProject.name = 'AwesomeApp'
- include ':react-native-firebase'
- project(':react-native-firebase').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-firebase/android')

include ':app'
```

#### Removing from Android Manifest

Open your _AndroidManifest.xml_ file. You will need to remove any references to the `io.invertase.firebase.messaging` class

```diff
- <service android:name="io.invertase.firebase.messaging.RNFirebaseMessagingService">
-   <intent-filter>
-     <action android:name="com.google.firebase.MESSAGING_EVENT" />
-   </intent-filter>
- </service>
```

#### Removing native dependencies

We now need to remove the RNFirebase and Firebase dependencies from your project.
In version 6, these are automatically installed for us.

Open your projects `/android/app/build.gradle` file. First remove the `react-native-firebase` dependency:

```diff
dependencies {
-   implementation project(path: ':react-native-firebase')
}
```

Next, remove the `firebase-core` and `play-services-base` dependencies. Note, other modules you are using may
required `play-services-base` to be installed.

_Specific versions listed may be different than your own project_

```diff
dependencies {
-   implementation "com.google.firebase:firebase-core:16.0.9"
-   implementation "com.google.android.gms:play-services-base:16.1.0"
}
```

Next we need to remove the module specific Firebase dependencies. The naming convention for these modules is:
`implementation "com.google.firebase:firebase-<< module >>:<<version>>"`.

For example, to remove the native Firebase dependency for the Authentication module:

```diff
dependencies {
-   implementation "com.google.firebase:firebase-auth:17.0.0"
}
```

#### Removing the React Native Firebase packages

We now need to remove the React Native Firebase packages from being added to our React Native application. Go ahead and open
the `/android/app/src/main/java/<< app name >>/MainApplication.java` file.

First, we need to remove the core `RNFirebasePackage` from the imports and being added to the package list:

```diff
-   import io.invertase.firebase.RNFirebasePackage;
```

```diff
    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
        new MainReactPackage(),
-       new RNFirebasePackage(),
```

Depending on what modules you installed using version 5, remove the packages for each module. For example, to remove the
Authentication package:

```diff
-   import io.invertase.firebase.auth.RNFirebaseAuthPackage;
```

```diff
    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
        new MainReactPackage(),
-       new RNFirebaseAuthPackage(),
```

#### Cleaning Gradle

Android caches module dependencies for quicker builds. As we've heavily modified our project dependencies it's recommended you
clean Gradle, allowing for a fresh installation. In your project, execute the following commands:

```bash
$ cd android/
$ ./gradlew clean
```

---

### Removing v5 from iOS

The following steps assume you have used CocoaPods to manage your projects iOS dependencies.

#### Remove the RNFirebase Pod

Remove the `RNFirebase` pod from your `/ios/Podfile`:

```diff
target 'AwesomeApp' do
-   pod 'RNFirebase', :path => '../node_modules/react-native-firebase/ios'
```

#### Remove the Firebase Core Pod

The `Firebase/Core` need to be removed from the project. In version 6, this is automatically installed. Open the
`/ios/Podfile` and remove the Pod:

```diff
target 'AwesomeApp' do
-   pod 'Firebase/Core', '~> 6.3.0'
```

#### Remove module specific Pods

Depending on what modules you were using with version 5, we now need to remove the Firebase Pods. For example, if you
are using the Authentication module, remove the `Firebase/Auth` Pod:

```diff
target 'AwesomeApp' do
-   pod 'Firebase/Auth', '~> 6.3.0'
```

### Re-installing Pods

Once the Pod dependencies have been removed, the following commands will remove the Pods from your local project:

```bash
$ cd ios
$ rm -rf Podfile.lock
$ pod install
```

---

## Installing `@react-native-firebase/app`

As mentioned earlier, version 6 uses the `@react-native-firebase` NPM organization for each module. Every app using
version 6 must install the `app` module before installing each specific module.

To get started, install the new dependency with [Yarn](https://yarnpkg.com/lang/en/):

```bash
yarn add @react-native-firebase/app
```

If you are using React Native 0.60+, the module will be automatically linked via [autolinking](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md).

Users on an older version of React Native must manually link the `app` module. See the following steps for [Android](/install-android) and
[iOS](/install-ios) for more information on manual linking.

## Specific module installation

Depending on which Firebase service your app uses, you now need to install the NPM packages for each service. For example,
apps using the Authentication module need to install the `auth` package:

```bash
yarn add @react-native-firebase/auth
```

Install the modules required for your application:

| Module                                                       | NPM Package                             |
| ------------------------------------------------------------ | --------------------------------------- |
| <Anchor href="v6/admob">AdMob</Anchor>                       | @react-native-firebase/admob            |
| <Anchor href="v6/analytics">Analytics</Anchor>               | @react-native-firebase/analytics        |
| <Anchor href="v6/app">App</Anchor>                           | @react-native-firebase/app              |
| <Anchor href="v6/invites">App Invites</Anchor>               | @react-native-firebase/invites          |
| <Anchor href="v6/auth">Authentication</Anchor>               | @react-native-firebase/auth             |
| <Anchor href="v6/firestore">Cloud Firestore</Anchor>         | @react-native-firebase/firestore        |
| <Anchor href="v6/functions">Cloud Functions</Anchor>         | @react-native-firebase/functions        |
| <Anchor href="v6/messaging">Cloud Messaging</Anchor>         | @react-native-firebase/messaging        |
| <Anchor href="v6/storage">Cloud Storage</Anchor>             | @react-native-firebase/storage          |
| <Anchor href="v6/crashlytics">Crashlytics</Anchor>           | @react-native-firebase/crashlytics      |
| <Anchor href="v6/in-app-messaging">In-app Messaging</Anchor> | @react-native-firebase/in-app-messaging |
| <Anchor href="v6/iid">Instance ID</Anchor>                   | @react-native-firebase/iid              |
| <Anchor href="v6/ml">ML</Anchor>                             | @react-native-firebase/ml               |
| <Anchor href="v6/perf">Performance Monitoring</Anchor>       | @react-native-firebase/perf             |
| <Anchor href="v6/database">Realtime Database</Anchor>        | @react-native-firebase/database         |
| <Anchor href="v6/remote-config">Remote Config</Anchor>       | @react-native-firebase/remote-config    |

Users on React Native version 0.60+, the modules will be automatically linked. For users on a lower version,
see the module specific pages for manual installation guides.

## Updating project code

In versions prior to 6, accessing the React Native Firebase package was carried out by importing the `react-native-firebase`
module, for example:

```js
import firebase from 'react-native-firebase';

// App code...
const user = firebase.auth().currentUser;
```

Although it is possible to access specific module functionality from the package imports, if you're coming from v5 the
following usage may seem daunting for a large project:

```js
import auth from '@react-native-firebase/auth';

// App code...
const user = auth().currentUser;
```

Fortunately, it is possible to continue to migrate to the previous versions import method:

Find and replace all usages of the import with the new import:

```diff
- import firebase from 'react-native-firebase';
+ import firebase from '@react-native-firebase/app';
```

We now need to import additional packages inside of an entry point file of our project, for example
to import the Authentication module, add the following to your projects `/App.js` file (or entry file):

```js
import firebase from '@react-native-firebase/app';
import '@react-native-firebase/auth';

// App code
const user = firebase.auth().currentUser;
```

This only needs to be done once. The `auth` module will now be available on all `firebase` instances.

---

## Module Breaking Changes

Below outlines a list of breaking changes for each module which may impact your application. Please ensure all
app functionality is tested once migrated to version 6 is complete.

### App

`@react-native-firebase/app`

- `onReady()` removed: Users initializing a secondary app via `app.initializeApp` will need to now remove the `onReady`
  listener. Instead, `initializeApp` resolves a promise once the secondary app has finished initializing.
- Initializing the `[DEFAULT]` app manually will now throw an error. Previously this only displayed a warning.

### AdMob

`@react-native-firebase/admob`

The AdMob module has undergone a full re-write to support a new, cleaner API and regulation changes (such as GDPR).
Please see the <Anchor href="/admob">AdMob</Anchor> documentation and update your code usage.

- `RewardedVideo` has now been deprecated in favor of a new native API. Please see `RewardedAd` for more information.

### Invites

The `invites` module has now been deprecated. Please see the official [Firebase Dynamic Links deprecation FAQ](https://firebase.google.com/support/dynamic-links-faq)
for more information.

The recommended approach for handling this deprecation is to use the Dynamic Links module.

### Analytics

`@react-native-firebase/analytics`

- All methods now return a `Promise`. Previously these were 'fire and forget'.

### Crashlytics

`@react-native-firebase/crashlytics`

- `setBoolValue`, `setFloatValue`, `setIntValue` & `setStringValue` have been removed and replaced with two new methods (the Crashlytics SDK converted all these into strings internally anyway):
  - `setAttribute(key: string, value: string): Promise<null>` - set a singular key value to show alongside any subsequent crash reports
  - `setAttributes(values: { [key: string]: string }): Promise<null>` - set multiple key values to show alongside any subsequent crash reports
- All methods except `crash`, `log` & `recordError` now return a Promise that resolve when complete.
- `recordError` now accepts a JavaScript `Error` instead of a code and message.
- `setUserIdentifier()` has been renamed to `setUserId()` to match the Analytics Web SDK implementation.
- `enableCrashlyticsCollection()` has been renamed to `setCrashlyticsCollectionEnabled()`.

### Firestore

`@react-native-firebase/firestore`

- The `Blob` class can no longer be manually constructed.
- All user code is now validated in `JavaScript`. Passing incorrect data or querying chaining will now throw a JavaScript error. Ensure all queries are thoroughly tested.
  - The `Query` class has undergone a rewrite. Previously some invalid queries could be passed to the native SDKs causing a crash, these are now validated in JavaScript.
- The where equal operator `=` has been deprecated. Please use `==`.
- The setting `setTimestampsInSnapshotsEnabled` has been deprecated.

### Dynamic Links

`@react-native-firebase/dynamic-links`

- Module usage has been renamed from `links()` to `dynamicLinks()`.
- The `onLink` and `getInitialLink` methods now return a `DynamicLink` object, rather than the string URL.
- The _builder_ syntax has been deprecated in favor of simple objects. See `buildLink()` documentation for an example.
- Added extra validation. Building a dynamic link with platform specific options will now error if not all required parameters are set.

### Functions

`@react-native-firebase/functions`

No breaking changes.

### In-App Messaging

`@react-native-firebase/in-app-messaging`

This is a new module. See documentation for usage.

### Instance ID

`@react-native-firebase/iid`

No breaking changes.

### Notifications

Device-local notification APIs are not actually Firebase APIs at the same time they are very difficult to maintain.

For these reasons the notifications package has been removed from react-native-firebase for versions 6 and higher.

How to migrate: If you use device-local notification APIs and user-visible notifications in your app you will want to integrate a separate library that gives you access to device-local notification APIs. Many people have reported success with each of https://notifee.app, https://wix.github.io/react-native-notifications and https://github.com/zo0r/react-native-push-notification

### Cloud Messaging

`@react-native-firebase/messaging`

- [android] The manually added `RNFirebaseMessagingService` service in your `AndroidManifest.xml` file is no longer required - you can safely remove it.
- [iOS] The manually added `RNFirebaseMessaging` usages in your `AppDelegate` files are no longer required - you can safely remove them.
- The _builder_ syntax has been deprecated in favor of simple objects. See `newRemoteMessage()` documentation for an example.
- `subscribeToTopic('some-topic')` method must not include "/" in topic.
- [iOS] The minimum supported iOS version is now 10
- iOS 9 or lower only accounts for 0.% of all iPhone devices.
- To see a detailed device versions breakdown see [this link](https://david-smith.org/iosversionstats/).
- Community contributions that add iOS 9 support are welcome.

### Performance Monitoring

`@react-native-firebase/perf`

- All `Trace` & `HttpMetric` methods (except for `start` & `stop`) are now synchronous and no longer return a Promise,
  extra attributes/metrics now only get sent to native when you call stop.
- `firebase.perf.Trace.incrementMetric` will now create a metric if it could not be found.
- `firebase.perf.Trace.getMetric` will now return 0 if a metric could not be found.

### Realtime Database

`@react-native-firebase/database`

- The `Reference` class has undergone a rewrite. In previous versions, chaining invalid methods together on a query was possible. In version 6, the functionality now replicates the Firebase Web SDK.
  - Please thoroughly test your database queries.
- Internal JavaScript validation has been added and will throw a JavaScript error if methods are called with incorrect parameters.
- All query based modifiers are now validated as per the Web SDK spec. In v5 it is possible to chain queries which are not allowed together causing native errors (e.g. `.orderByKey().orderByPriority()`, `.startAt('foo', 'bar').orderByKey()` etc). Doing so in v6 will now throw an error to keep it in-line with the Web SDK.
- `Reference.push` now correctly mimics the Web SDK, returning a thenable reference.

### Remote Config

`@react-native-firebase/remote-config`

- Module namespace has been renamed to `.remoteConfig()` from `.config()`.
- All Remote Config values can now be accessed synchronously in JS, see `getValue(key: string): ConfigValue` & `getAll(): ConfigValues` below.
  - These replace all the original async methods: `getValue`, `getValues`, `getKeysByPrefix`.
- `setDefaultsFromResource` now returns a Promise that resolves when completed, this will reject with code `config/resource_not_found` if the file could not be found.
- `setDefaultsFromResource` now expects a resource file name for Android to match iOS, formerly this required a resource id (something you would not have in RN as this was generated at build time by Android).
  - An example for both platforms can be found in the tests.
- `enableDeveloperMode` has been removed, you can now use `setConfigSettings({ isDeveloperModeEnabled: boolean })` instead.
- `setDefaults` now returns a Promise that resolves when completed.

### Cloud Storage

`@react-native-firebase/storage`

- Removed formerly deprecated `UploadTaskSnapshot.downloadUrl` property, use `StorageReference.getDownloadURL(): Promise<string>` instead.
- `StorageReference.downloadFile()` is now deprecated and will be removed in a later release, please rename usages of this to `writeToFile()` - renamed to match Native SDKs.
- `firebase.storage.Native` has moved to `firebase.utils.Native`.
- `firebase.utils.Native` is now deprecated and will be removed in a later release, please rename usages of this to `firebase.utils.FilePath`.
- `firebase.utils.Native.*` some properties have been renamed and deprecated and will be removed in a later release, follow the in-app console warnings on how to migrate.

### ML

`@react-native-firebase/ml`

This is a new module. See documentation for usage.
```

### Migrating to v22

Source: https://rnfirebase.io/migrating-to-v22

```mdx

# Switching off warning logs

You may notice a lot of console warning logs as we deprecate the existing namespaced API. If you would like to switch these logs off, you may set the following global property to `true` anywhere before you initialize Firebase.

```js
globalThis.RNFB_SILENCE_MODULAR_DEPRECATION_WARNINGS = true;
```

# Enabling deprecation strict modes

You may enable a feature for the API migration which will throw a javascript error immediately when any namespaced API usage is detected.

This is useful to help you quickly locate any remaining usage of the deprecated namespace API via examination of the line numbers in the stack trace.

Note that there may be modular API implementation errors within the react-native-firebase modules, this may still be useful as a troubleshooting aid when collaborating with the maintainers to correct these errors.

```js
globalThis.RNFB_MODULAR_DEPRECATION_STRICT_MODE = true;
```

# Migrating to React Native modular API

If you are familiar with the Firebase JS SDK, the upgrade will be a familiar process, following similar steps to [the migration guide](https://firebase.google.com/docs/web/modular-upgrade#refactor_to_the_modular_style) for firebase-js-sdk.

React Native Firebase uses the same API as the official [Firebase JS SDK modular API documentation](https://firebase.google.com/docs/reference/js) so the same migration steps apply here, except there is no need for special "compat" imports as an intermediate step.

The process will always follow the same steps for every API you use:

- determine the new modular API function for the old namespaced API you are using
- import that new modular API function
- change the call from using the firebase module to access the API and passing parameters, to the new style of using the modular API function, passing in the firebase module object(s) required for it to work and then the parameters.

In the end, it should be a very mechanical process and can be done incrementally, one API call at a time.

There are concrete examples below to show the process

## Firestore Deprecation Example

### Namespaced (deprecated) Query

You ought to move away from the following way of making Firestore queries. The React Native Firebase namespaced API is being completely removed in React Native Firebase v22:

```js
import firestore from '@react-native-firebase/firestore';

const db = firestore();

const querySnapshot = await db.collection('cities').where('capital', '==', true).get();

querySnapshot.forEach(doc => {
  console.log(doc.id, ' => ', doc.data());
});
```

### Modular Query

This is how the same query would look using the new, React Native Firebase modular API:

```js
import { collection, query, where, getDocs, getFirestore } from '@react-native-firebase/firestore';

const db = getFirestore();

const q = query(collection(db, 'cities'), where('capital', '==', true));

const querySnapshot = await getDocs(q);

querySnapshot.forEach(doc => {
  console.log(doc.id, ' => ', doc.data());
});
```

For more examples of requesting Firestore data, see the official Firebase documentation for [Get data with Cloud Firestore](https://firebase.google.com/docs/firestore/query-data/get-data).

### Migration Help

You will find code snippets for "Web namespaced API" and "Web modular API" throughout the official Firebase documentation. Update your code to use "Web modular API". Here are some links to help you get started:

- [Firestore](https://firebase.google.com/docs/firestore/quickstart)
- [Auth](https://firebase.google.com/docs/auth/web/start)
- [RTDB](https://firebase.google.com/docs/database/web/start)
- [Storage](https://firebase.google.com/docs/storage/web/start)
- [Remote Config](https://firebase.google.com/docs/remote-config/get-started?platform=web)
- [Messaging](https://firebase.google.com/docs/cloud-messaging/js/client)
- [Functions](https://firebase.google.com/docs/functions/callable)
- [App Check](https://firebase.google.com/docs/app-check/web/recaptcha-provider)
- [Analytics](https://firebase.google.com/docs/analytics/get-started)
- [Perf](https://firebase.google.com/docs/perf-mon/get-started-web)
- [Crashlytics](https://github.com/invertase/react-native-firebase/blob/ae03f3f0be636fcd949965ee720a691f8582ef82/packages/crashlytics/lib/types/crashlytics.ts) (Crashlytics doesn't exist on Firebase web, this is a link to the type declarations which contains all methods available).
```

### Migrating to v23

Source: https://rnfirebase.io/migrating-to-v23

```mdx

# Firebase Crashlytics

Modular API method `isCrashlyticsCollectionEnabled(crashlytics)` has been removed, please use the property on Crashlytics instance
`getCrashlytics().isCrashlyticsCollectionEnabled` instead.

# Firebase Auth

`MultiFactorUser.enrolledFactor` has been removed, please use `MultiFactorUser.enrolledFactors`. See example:

```js
const credential = await signInWithEmailAndPassword(getAuth(), 'dummy@example.com', 'password');
const multiFactorUser = credential.user.multiFactor;
// Use below - remove any instances of `multiFactorUser.enrolledFactor`
console.log(multiFactorUser.enrolledFactors);
```

# Firebase App

- `gaMeasurementId` property from `FirebaseOptions` has been replaced with `measurementId` to match Firebase web JS SDK.

# Firebase Dynamic Links

⚠️ **REMOVED** ⚠️

Firebase Dynamic Links has been Removed

This package has been deprecated and removed from the React Native Firebase repository.

## Why was it deprecated?

Firebase Dynamic Links has been deprecated by Google and will be shut down on August 25th, 2025. For more information about the deprecation and migration options, please visit:

**[Firebase Dynamic Links Deprecation FAQ](https://firebase.google.com/support/dynamic-links-faq)**

## Migration Options

The deprecation FAQ provides detailed guidance on how to migrate from Firebase Dynamic Links, including:

- **Full feature parity**: Use alternative deep-linking service providers
- **Simple deep-linking**: Migrate to App Links and Universal Links. [See Firebase documentation](https://firebase.google.com/support/guides/app-links-universal-links).
- **No replacement needed**: Remove the package entirely

## Timeline

- **August 25th, 2025**: Firebase Dynamic Links service will be completely shut down
- All existing links will stop working
- All APIs will return error responses

Please refer to the official deprecation FAQ for complete migration guidance and support.

# Android Platform

- Android `minSdk` has been bumped from `21` to `23` (except Auth which already had a `minSdk` of `23`).
- Auth play services has been bumped from `21.3.0` to `21.4.0`.
- Crashlytics gradle plugin has been bumped from `3.0.4` to `3.0.5`.
- Performance gradle plugin has been bumped from `1.4.2` to `2.0.0`.
- App distribution gradle plugin has been bumped from `5.1.0` to `5.1.1`.

# iOS platform

- Minimum iOS deployment target has now been bumped to `15` from `13`.
- Minimum Xcode version required for iOS development is now Xcode `16.2`, previous was Xcode `15.2`.
- `gaMeasurementId` property from `FirebaseOptions` (now `measurementId` in React Native Firebase) has been removed from firebase-ios-sdk as it wasn't used.

# Web platform

- Firebase JS SDK has been bumped to `12.0.0` which now requires a minimum of Node.js `20` and a minimum of `ES2020`.
  [See release notes](https://firebase.google.com/support/release-notes/js#version_1200_-_july_17_2025).
```

### Migrating to v24

Source: https://rnfirebase.io/migrating-to-v24

```mdx

Version 24 includes breaking changes for Firestore TypeScript types and Cloud Functions native integration. Review the sections below for the modules you use.

# Firestore

Version 24 introduces `withConverter` functionality from Firebase JS SDK. Due to the differences in types between references and queries in namespace vs modular API, and the namespaced APIs deprecation cycle being effectively complete with the API set for removal, we have adopted the modular API typing in general for firestore APIs.

Reference and query types have been updated to support input of two generic types (`AppModelType`, `DbModelType`).

Additionally, to match the JS SDK, they are now exported separately at the root, instead of through `FirebaseFirestoreTypes`.

Most commonly these types will be affected: `CollectionReference`, `DocumentReference`, `DocumentSnapshot`, `QueryDocumentSnapshot`, `QuerySnapshot`, `Query`.

```js
// Previously
import { doc, getFirestore, onSnapshot, FirebaseFirestoreTypes } from '@react-native-firebase/firestore';

onSnapshot(doc(getFirestore(), 'foo', 'foo'), {
  next: (snapshot: FirebaseFirestoreTypes.DocumentSnapshot) => {
    console.log(snapshot.get('foo'));
  },
});
```

```js
// Now
import { doc, getFirestore, onSnapshot, DocumentSnapshot } from '@react-native-firebase/firestore';

onSnapshot(doc(getFirestore(), 'foo', 'foo'), {
  next: (snapshot: DocumentSnapshot) => {
    console.log(snapshot.get('foo'));
  },
});
```

# Cloud Functions

**PR:** [#8603](https://github.com/invertase/react-native-firebase/pull/8603) / v24.0.0 ([#8799](https://github.com/invertase/react-native-firebase/issues/8799) streaming callables)

From v24 onward, `@react-native-firebase/functions` is implemented as a **TurboModule** and **requires React Native's New Architecture**. The legacy bridge module was removed.

## Who is affected

| You use…                                                          | Action                                                                                                                                         |
| ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `@react-native-firebase/functions` on **New Architecture**        | No change required                                                                                                                             |
| `@react-native-firebase/functions` on the **legacy architecture** | Enable New Architecture, or stay on v23 if you cannot migrate yet                                                                              |
| Other RN Firebase modules only                                    | Functions is the first module with a hard requirement; `@react-native-firebase/app` prints a deprecation warning on legacy architecture builds |

## Enable New Architecture

**React Native CLI (Android)** — set in `android/gradle.properties`:

```properties
newArchEnabled=true
```

**React Native CLI (iOS)** — New Architecture is enabled when `RCT_NEW_ARCH_ENABLED=1` during `pod install` (React Native sets this from `newArchEnabled` on recent templates). Follow the [React Native New Architecture guide](https://reactnative.dev/docs/the-new-architecture/landing-page) for your RN version.

**Expo** — enable in your app config (Expo SDK 52+):

```json
{
  "expo": {
    "newArchEnabled": true
  }
}
```

Rebuild native projects after changing this (`pod install`, clean Android build).

## If New Architecture is disabled

- **Android:** the Functions Gradle script fails the build with `New Architecture support is required for @react-native-firebase/functions`.
- **iOS:** `pod install` fails with `RNFBFunctions requires New Architecture. Enable New Architecture to use this module`.

## API notes

The JavaScript API (`functions()`, `httpsCallable`, modular helpers) is unchanged. v24 also adds [`httpsCallable().stream()`](/functions/usage) support, which relies on the TurboModule implementation.

Other React Native Firebase modules still run on legacy architecture in v24, but old architecture support is deprecated project-wide and will be required for additional modules in future releases.
```

### Migrating to v25

Source: https://rnfirebase.io/migrating-to-v25

```mdx

Version 25 completes the TypeScript alignment started in v24. Modular types across multiple packages now match the [firebase-js-sdk](https://firebase.google.com/docs/web/modular-upgrade) modular API as closely as possible. Runtime behavior is largely unchanged; **TypeScript consumers** and apps using the **modular API** are most affected.

If you upgraded to v24 for Firestore only, see [Migrating to v24](/migrating-to-v24) first — Firestore breaking changes remain in v24.

## Table of contents

- [Why we made these changes](/migrating-to-v25#why-we-made-these-changes)
- [Agent-assisted migration](/migrating-to-v25#agent-assisted-migration)
- [Who is affected](/migrating-to-v25#who-is-affected)
- [General pattern](/migrating-to-v25#general-pattern)
- [Tooling & native SDKs](/migrating-to-v25#tooling--native-sdks)
- [Cloud Storage](/migrating-to-v25#cloud-storage)
- [Realtime Database](/migrating-to-v25#realtime-database)
- [Remote Config](/migrating-to-v25#remote-config)
- [Performance Monitoring](/migrating-to-v25#performance-monitoring)
- [Installations](/migrating-to-v25#installations)
- [App Check](/migrating-to-v25#app-check)
- [Firebase Auth](/migrating-to-v25#firebase-auth)
- [Cloud Messaging](/migrating-to-v25#cloud-messaging)
- [Automated migration checklist](/migrating-to-v25#automated-migration-checklist)

## Why we made these changes

React Native Firebase aims to be a drop-in replacement for the [firebase-js-sdk](https://firebase.google.com/docs/web/setup) — with native extensions and performance where the platform allows. At the JavaScript level we have always tracked the modular API closely, but our TypeScript declarations had diverged: namespaces specific to React Native Firebase, instance-style typings, and helper names that did not match the SDK.

v25 finishes aligning those types module by module. For you as a developer that means:

- **Shared mental model** — code, examples, and AI assistance written for firebase-js-sdk modular APIs map directly to React Native Firebase.
- **Safer refactors** — TypeScript catches incorrect imports and call shapes at compile time instead of at runtime.
- **Easier cross-platform work** — the same typed modular surface works across web, React Native, and shared business logic.
- **A clear path forward** — namespaced APIs remain for compatibility but modular root exports are the supported, typed source of truth.

Most apps behave the same after updating package versions; the work is primarily import and type adjustments where TypeScript reports errors.

## Agent-assisted migration

This guide is written to be **complete enough for an automated first pass**. We recommend feeding this document to your coding agent, then asking it to analyze your codebase against each relevant section below and produce a concrete change list (or PR) for the packages you use. The [Automated migration checklist](/migrating-to-v25#automated-migration-checklist) at the end is structured for that workflow.

## Who is affected

| You use…                                                      | Likely impact                                                                        |
| ------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| TypeScript + modular imports (`getX()`, `ref()`, etc.)        | **High** — update imports and call patterns per package sections below               |
| TypeScript + namespaced API only (`firebase.storage().ref()`) | **Low–medium** — namespaced APIs remain; some types are deprecated or stricter       |
| JavaScript only, no type checking                             | **Low** — runtime is mostly compatible; deprecated APIs still work but emit warnings |

## General pattern

Across v25 package migrations:

1. Import **modular types and functions from the package root**, not from `Firebase*Types` namespaces.
2. Prefer **free functions** (`getToken(appCheck)`, `trace(perf, name)`) over instance methods on service objects.
3. `Firebase*Types` namespaces remain for namespaced compatibility but are **deprecated** for new code.
4. Namespaced APIs (`firebase.auth()`, `storage()`) still work; modular is the typed source of truth.

# Tooling & native SDKs

**Commit:** `c8c1fc105` (SDK bump)

| Change                                                    | Who            | Action                                      |
| --------------------------------------------------------- | -------------- | ------------------------------------------- |
| `firebase-ios-sdk` 12.12.0+ requires **Xcode 26.2+**      | iOS developers | Upgrade Xcode before building v25           |
| `firebase-android-sdk` 34.12.0, `firebase-js-sdk` 12.12.0 | All            | Update lockfiles / pods as usual on upgrade |

# Cloud Storage

**PR:** [#8824](https://github.com/invertase/react-native-firebase/pull/8824)

Modular Storage types now match firebase-js-sdk. The namespaced API (`firebase.storage()`, `FirebaseStorageTypes`) is preserved separately and deprecated for new code.

## Type & export changes

| Before (v24 modular / types)                                          | Now (v25)                                                                    |
| --------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| Instance methods on `Storage` / `StorageReference` in modular typings | Use root-level functions: `ref()`, `uploadBytes()`, `getDownloadURL()`, etc. |
| `refFromURL(storage, url)`                                            | `ref(storage, url)` — `ref()` accepts `gs://` and `https://` URLs            |
| `child(ref, path)`                                                    | `ref(ref, path)`                                                             |
| Modular `toString(ref)` helper                                        | `ref.toString()` on the reference                                            |
| `storage.statics.StringFormat`, `TaskEvent`, `TaskState`              | Import `StringFormat`, `TaskEvent`, `TaskState` from package root            |
| `md5hash` in metadata                                                 | `md5Hash` (firebase-js-sdk spelling)                                         |
| Generic `Error` in task callbacks                                     | `NativeFirebaseError`                                                        |
| `ListOptions.pageToken` required `string`                             | `string \| null` (nullable, matching firebase-js-sdk)                        |

## Example: modular references and uploads

```js
// Previously
import { getStorage, refFromURL, child } from '@react-native-firebase/storage';

const storage = getStorage();
const fileRef = child(refFromURL(storage, 'gs://bucket/path/file.jpg'), 'thumb.jpg');
await fileRef.putFile(localPath);
```

```js
// Now
import { getStorage, ref, putFile } from '@react-native-firebase/storage';

const storage = getStorage();
const fileRef = ref(ref(storage, 'gs://bucket/path/file.jpg'), 'thumb.jpg');
await putFile(fileRef, localPath);
```

## Example: types and task constants

```js
// Previously
import { getStorage, FirebaseStorageTypes } from '@react-native-firebase/storage';

function onState(snapshot: FirebaseStorageTypes.TaskSnapshot) {
  if (snapshot.state === getStorage().app.storage().constructor.TaskState.RUNNING) { /* … */ }
}
```

```js
// Now
import { getStorage, ref, uploadBytesResumable, TaskState, type TaskSnapshot } from '@react-native-firebase/storage';

function onState(snapshot: TaskSnapshot) {
  if (snapshot.state === TaskState.RUNNING) { /* … */ }
}
```

**RN-only helpers** (`putFile`, `writeToFile`) remain exported from the package root for native file paths.

# Realtime Database

**PR:** [#8977](https://github.com/invertase/react-native-firebase/pull/8977)

Modular RTDB types (`DatabaseReference`, `Query`, `DataSnapshot`, `OnDisconnect`, `QueryConstraint`) no longer expose namespaced instance-style methods in public typings. Use function-based modular helpers.

## Breaking changes

| Before                                                          | Now                                            |
| --------------------------------------------------------------- | ---------------------------------------------- |
| `import { ServerValue } from '@react-native-firebase/database'` | Use `serverTimestamp()` and `increment(delta)` |
| `await goOffline(db)` / `.then()` on `goOffline`                | `goOffline(db)` returns `void`                 |
| `await goOnline(db)`                                            | `goOnline(db)` returns `void`                  |
| `getServerTime(db)` treated as async                            | Returns synchronous `Date`                     |

## Example: server timestamps

```js
// Previously
import { getDatabase, ref, set, ServerValue } from '@react-native-firebase/database';

await set(ref(getDatabase(), 'posts/1'), { createdAt: ServerValue.TIMESTAMP });
```

```js
// Now
import { getDatabase, ref, set, serverTimestamp } from '@react-native-firebase/database';

await set(ref(getDatabase(), 'posts/1'), { createdAt: serverTimestamp() });
```

## Example: modular query helpers

```js
// Previously — instance methods typed on modular references
import {
  getDatabase,
  ref,
  query,
  orderByChild,
  equalTo,
  onValue,
} from '@react-native-firebase/database';

const db = getDatabase();
const scoresRef = ref(db, 'scores');
const q = query(scoresRef, orderByChild('score'), equalTo(100));
onValue(q, snapshot => {
  /* … */
});
```

Function-based helpers (`query`, `orderByChild`, `onValue`, etc.) are unchanged at runtime; **TypeScript** may now require you to stop calling deprecated instance methods (`.orderByChild()`, `.on()`) on modular-typed references and use the modular functions instead.

Namespaced `firebase.database.ServerValue` remains available.

# Remote Config

**PR:** [#8972](https://github.com/invertase/react-native-firebase/pull/8972)

Modular Remote Config now uses firebase-js-sdk type names and instance properties.

## Removed / renamed modular API

| Removed (v24)                                                                                                                                               | Replacement (v25)                                                             |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `fetch(remoteConfig, expirationDurationSeconds?)`                                                                                                           | `fetchConfig(remoteConfig)`                                                   |
| `setConfigSettings(remoteConfig, settings)`                                                                                                                 | `remoteConfig.settings = { minimumFetchIntervalMillis, fetchTimeoutMillis }`  |
| `setDefaults(remoteConfig, defaults)`                                                                                                                       | `remoteConfig.defaultConfig = { … }`                                          |
| `onConfigUpdated(remoteConfig, cb)`                                                                                                                         | `onConfigUpdate(remoteConfig, observer)`                                      |
| `fetchTimeMillis()`, `settings()`, `lastFetchStatus()` helpers                                                                                              | Read `remoteConfig.fetchTimeMillis`, `.settings`, `.lastFetchStatus`          |
| `RemoteConfigValue.value` / `.source` getters                                                                                                               | `value.asString()` (etc.) and `value.getSource()`                             |
| Exports: `LastFetchStatus`, `ValueSource`, `ConfigSettings`, `ConfigDefaults`, `ConfigValue`, `ConfigValues`, `LastFetchStatusType`, `RemoteConfigLogLevel` | Use `FetchStatus`, `ValueSource`, `Value`, `RemoteConfigSettings`, `LogLevel` |

**Settings field rename:** modular `RemoteConfigSettings` uses `fetchTimeoutMillis` (firebase-js-sdk), not the older React Native Firebase style `fetchTimeMillis` on the modular surface.

## Example: fetch and read values

```js
// Previously
import {
  getRemoteConfig,
  fetch,
  activate,
  getValue,
  FirebaseRemoteConfigTypes,
} from '@react-native-firebase/remote-config';

const rc = getRemoteConfig();
await fetch(rc, 3600);
await activate(rc);
const flag = getValue(rc, 'feature_enabled');
console.log(flag.value, flag.source);
```

```js
// Now
import {
  getRemoteConfig,
  fetchConfig,
  activate,
  getValue,
  type Value,
} from '@react-native-firebase/remote-config';

const rc = getRemoteConfig();
rc.settings = {
  minimumFetchIntervalMillis: 3600000,
  fetchTimeoutMillis: 60000,
};
await fetchConfig(rc);
await activate(rc);
const flag: Value = getValue(rc, 'feature_enabled');
console.log(flag.asString(), flag.getSource());
```

# Performance Monitoring

**PR:** `4aedfe883` (TypeScript migration)

## Breaking changes

| Before                                                 | Now                                                                     |
| ------------------------------------------------------ | ----------------------------------------------------------------------- |
| `await initializePerformance(app, settings)`           | Returns `FirebasePerformance` **synchronously**                         |
| `perf.newTrace(name)`, `perf.startTrace(name)`         | `trace(perf, name)`                                                     |
| `perf.newHttpMetric(url, method)`                      | `httpMetric(perf, url, method)`                                         |
| `perf.newScreenTrace(name)` / `startScreenTrace(name)` | `newScreenTrace(perf, name)` / `startScreenTrace(perf, name)`           |
| `perf.setPerformanceCollectionEnabled(bool)`           | `perf.dataCollectionEnabled = bool`                                     |
| `PerformanceSettings` React Native Firebase shape      | `{ dataCollectionEnabled?, instrumentationEnabled? }` (firebase-js-sdk) |
| `trace.getAttribute(key)` typed as `string \| null`    | `string \| undefined`                                                   |

**RN-only exports retained:** `httpMetric`, `newScreenTrace`, `startScreenTrace`, `HttpMethod`, `HttpMetric`, `ScreenTrace`.

## Example

```js
// Previously
import { getPerformance } from '@react-native-firebase/perf';

const perf = getPerformance();
const t = perf.newTrace('load_screen');
await t.start();
```

```js
// Now
import { getPerformance, trace } from '@react-native-firebase/perf';

const perf = getPerformance();
const t = trace(perf, 'load_screen');
await t.start();
```

# Installations

**PR:** `739a4ca36` (TypeScript migration)

Modular `getInstallations()` returns a firebase-js-sdk-shaped `Installations` object exposing only `app`. Use modular helper functions instead of instance methods.

## Breaking changes

| Before                     | Now                                                             |
| -------------------------- | --------------------------------------------------------------- |
| `installations.getId()`    | `getId(installations)`                                          |
| `installations.getToken()` | `getToken(installations)`                                       |
| `installations.delete()`   | `deleteInstallations(installations)` — argument is **required** |

## Example

```js
// Previously
import { getInstallations } from '@react-native-firebase/installations';

const installations = getInstallations();
const id = await installations.getId();
```

```js
// Now
import { getInstallations, getId } from '@react-native-firebase/installations';

const installations = getInstallations();
const id = await getId(installations);
```

Namespaced `firebase.installations()` / `FirebaseInstallationsTypes` remain available (deprecated).

# App Check

**PR:** [#8889](https://github.com/invertase/react-native-firebase/pull/8889)

Version 25 aligns App Check's modular exports more closely with the Firebase JS SDK. If your app uses the modular API, import App Check types and helpers directly from `@react-native-firebase/app-check` instead of routing modular code through `FirebaseAppCheckTypes`.

The most common updates are:

- Import modular helpers such as `initializeAppCheck`, `getToken`, `getLimitedUseToken`, `setTokenAutoRefreshEnabled`, and `onTokenChanged` from the package root.
- Import modular types such as `AppCheck` and `AppCheckTokenResult` from the package root.
- `FirebaseApp` is no longer exported from `@react-native-firebase/app-check`; import it from `@react-native-firebase/app`.
- `FirebaseAppCheckTypes` is **type-only** — use `import type { FirebaseAppCheckTypes }`.
- Modular `AppCheck` has no instance methods (matching firebase-js-sdk); use free functions.
- `onTokenChanged` callback receives `AppCheckTokenResult`, not `AppCheckListenerResult`.
- Keep using `ReactNativeFirebaseAppCheckProvider` on React Native when you need native provider selection for Android / Apple / web.

```js
// Previously
import appCheck, { FirebaseAppCheckTypes } from '@react-native-firebase/app-check';

const instance = appCheck();

instance.getToken().then((result: FirebaseAppCheckTypes.AppCheckTokenResult) => {
  console.log(result.token);
});
```

```js
// Now
import { getApp } from '@react-native-firebase/app';
import {
  AppCheckTokenResult,
  ReactNativeFirebaseAppCheckProvider,
  initializeAppCheck,
  getToken,
} from '@react-native-firebase/app-check';

const provider = new ReactNativeFirebaseAppCheckProvider();

provider.configure({
  android: {
    provider: __DEV__ ? 'debug' : 'playIntegrity',
  },
  apple: {
    provider: __DEV__ ? 'debug' : 'appAttestWithDeviceCheckFallback',
  },
  web: {
    provider: 'reCaptchaV3',
    siteKey: 'your-recaptcha-site-key',
  },
});

const appCheck = await initializeAppCheck(getApp(), {
  provider,
  isTokenAutoRefreshEnabled: true,
});

const result: AppCheckTokenResult = await getToken(appCheck);
console.log(result.token);
```

If you do not need to reuse a provider instance, you can now also pass the React Native provider configuration inline through `providerOptions`:

```js
import { getApp } from '@react-native-firebase/app';
import { initializeAppCheck } from '@react-native-firebase/app-check';

await initializeAppCheck(getApp(), {
  provider: {
    providerOptions: {
      android: {
        provider: __DEV__ ? 'debug' : 'playIntegrity',
      },
      apple: {
        provider: __DEV__ ? 'debug' : 'appAttestWithDeviceCheckFallback',
      },
      web: {
        provider: 'reCaptchaV3',
        siteKey: 'your-recaptcha-site-key',
      },
    },
  },
  isTokenAutoRefreshEnabled: true,
});
```

# Firebase Auth

**PR:** [#8991](https://github.com/invertase/react-native-firebase/pull/8991)

Version 25 aligns `@react-native-firebase/auth` TypeScript types with the firebase-js-sdk modular API. Runtime behavior is largely unchanged, but TypeScript consumers should review the following breaking changes.

For maintainers and coding agents: the living triage matrix is [`okf-bundle/packages/auth/compare-types-triage.md`](https://github.com/invertase/react-native-firebase/blob/main/okf-bundle/packages/auth/compare-types-triage.md). Run `yarn compare:types auth` after public API edits and update `.github/scripts/compare-types/configs/auth.ts` when differences are intentional.

## Platform matrix (read before changing Auth)

| Context          | `Platform.OS`          | Backend                                 | Notes                                                                                                                    |
| ---------------- | ---------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| **iOS/Android**  | `ios`, `android`       | Native Firebase Auth SDK                | Native bridge types (`verifyPhoneNumber` listener, Multi-Factor Authentication overloads, async `isSignInWithEmailLink`) |
| **Other/Hermes** | e.g. macOS, Windows RN | firebase-js-sdk via the auth web bridge | No DOM; MFA/TOTP covered by `tests/local-tests`                                                                          |
| **Other/Web**    | browser embedding      | firebase-js-sdk                         | DOM APIs (reCAPTCHA, redirect) possible but not all delegated yet                                                        |

When a symbol is documented as **iOS/Android only**, do not assume it throws or is missing on Other without checking the web bridge. When compare:types signatures match but runtime differs, document in triage / this guide (not necessarily in `differentShape`).

## Modular types

Import modular types directly from `@react-native-firebase/auth` instead of `FirebaseAuthTypes` where possible. The namespaced `FirebaseAuthTypes` namespace remains available but is deprecated.

For auth errors, use `NativeFirebaseAuthError` (or the modular `AuthError` interface) instead of expecting a firebase-js-sdk `AuthError` class export — React Native Firebase does not re-export the firebase-js-sdk error class.

## Removed modular export

- `initializeRecaptchaConfig` is not exported. React Native Firebase uses native SDK Phone Auth verification rather than the browser reCAPTCHA bootstrap flow.

## Deprecated provider helpers

The following RN Firebase-specific provider classes are **deprecated in v25**. Use `OAuthProvider` instead (matching firebase-js-sdk):

| Deprecated          | Replacement                                    |
| ------------------- | ---------------------------------------------- |
| `AppleAuthProvider` | `new OAuthProvider('apple.com')`               |
| `OIDCAuthProvider`  | `new OAuthProvider('oidc.<your-provider-id>')` |
| `OIDCProvider`      | `OAuthProvider`                                |

```js
// Previously (deprecated)
import { AppleAuthProvider } from '@react-native-firebase/auth';
const credential = AppleAuthProvider.credential(idToken, rawNonce);

// Now
import { OAuthProvider } from '@react-native-firebase/auth';
const provider = new OAuthProvider('apple.com');
const credential = provider.credential({ idToken, rawNonce });
```

```js
// Previously (deprecated)
import { OIDCAuthProvider } from '@react-native-firebase/auth';
const credential = OIDCAuthProvider.credential('sample-provider', idToken, accessToken);

// Now
import { OAuthProvider } from '@react-native-firebase/auth';
const provider = new OAuthProvider('oidc.sample-provider');
const credential = provider.credential({ idToken, accessToken });
```

`AppleAuthProvider` and `OIDCAuthProvider` remain exported for compatibility but will be removed in a future major release.

## Action code URL parsing

`ActionCodeURL.parseLink` and `parseActionCodeURL` are implemented as **synchronous** pure URL parsers (matching firebase-js-sdk). They work on all platforms without calling the native bridge.

## Signature changes

- `isSignInWithEmailLink(auth, emailLink)` — returns `Promise<boolean>` on iOS/Android (native bridge). The firebase-js-sdk returns a synchronous `boolean`. Port web code with `await isSignInWithEmailLink(auth, link)` (or `.then(...)`).
- `sendSignInLinkToEmail(auth, email, actionCodeSettings)` — `actionCodeSettings` is **required** in the modular API (matching firebase-js-sdk).
- **Namespaced email link (react-native-firebase convenience):** `firebase.auth().sendSignInLinkToEmail(email, settings?)` still accepts omitted settings. Internally `_resolveActionCodeSettings()` defaults `url` from `app.options.authDomain` and `handleCodeInApp: true`. This is **not** platform-specific — only namespaced vs modular. Do not “fix” modular to match namespaced defaults.
- `signInWithEmailLink(auth, email, emailLink?)` — the third argument is optional, matching firebase-js-sdk.
- `signInWithRedirect` / `linkWithRedirect` — return `Promise<UserCredential>` on native because provider flows resolve immediately with credentials instead of following the browser redirect contract.
- `reauthenticateWithRedirect` — returns `Promise<void>` on native while still updating `currentUser` after the native provider flow completes.
- `connectAuthEmulator(auth, url, options?)` — when `options` is provided, `disableWarnings` is required (matching firebase-js-sdk).

## Web APIs with matching types but different native runtime (iOS/Android)

These modular helpers are exported for firebase-js-sdk API parity. **On iOS/Android they throw synchronously** (or are not applicable) because native SDKs do not implement the browser persistence, redirect, or reCAPTCHA phone-link flows. Types match the firebase-js-sdk; behavior does not — see API reference `@remarks` on each symbol.

| API                             | Native iOS/Android behavior                                                                           |
| ------------------------------- | ----------------------------------------------------------------------------------------------------- |
| `getRedirectResult`             | Always throws — use immediate `UserCredential` from `signInWithRedirect` / `linkWithRedirect` instead |
| `setPersistence`                | Always throws — native SDKs manage auth state                                                         |
| `useDeviceLanguage`             | Always throws                                                                                         |
| `revokeAccessToken`             | Always throws                                                                                         |
| `linkWithPhoneNumber`           | Always throws                                                                                         |
| `reauthenticateWithPhoneNumber` | Always throws                                                                                         |

`initializeAuth(app, deps?)` accepts the firebase-js-sdk `Dependencies` type for API parity but **ignores** persistence, popup redirect resolver, and error-map dependencies on iOS/Android (see below).

## Phone Auth (iOS/Android)

- `verifyPhoneNumber(auth, phoneNumber, ...)` — **iOS/Android only** native listener flow (force-resend, auto-verification callbacks). On Other platforms use `signInWithPhoneNumber` / firebase-js-sdk `PhoneAuthProvider` instead.
- `signInWithPhoneNumber(auth, phoneNumber, appVerifier?)` — modular API no longer accepts the former `forceResend` fourth argument from React Native Firebase; use `verifyPhoneNumber` when you need the native listener / force-resend behavior.

## Sign in with Apple

- `revokeToken(auth, authorizationCode)` — React Native Firebase-specific modular helper for Apple's account-deletion requirement. **Supported on iOS** (native `revokeTokenWithAuthorizationCode`). Android and Web bridges resolve without performing revocation.
- Do not confuse with `revokeAccessToken` — that is firebase-js-sdk web-only OAuth token revocation and always throws on iOS/Android.
- The namespaced `firebase.auth().revokeToken(authorizationCode)` API remains available but is deprecated.

## Auth instance surface

- `auth.tenantId = 'tenant-id'` is now supported (delegates to `setTenantId`).
- `auth.authStateReady()`, `auth.beforeAuthStateChanged(...)`, `auth.emulatorConfig`, and `auth.updateCurrentUser(user)` are implemented on the Auth instance.
- **`auth.config` runtime split (types unified):** Declarations match firebase-js-sdk `Config`, but runtime differs by platform:
  - **iOS/Android:** always `{}` — native SDKs do not expose the web config object.
  - **Other (Hermes/Web):** firebase-js-sdk can populate `auth.config`, but React Native Firebase does not delegate this yet. Do not read `auth.config` on native expecting `apiKey` / `authDomain`; use `getCustomAuthDomain(auth)` on iOS/Android or app options on Other until delegation lands.

## Credential providers

Provider credential factories return firebase-js-sdk-shaped credential **classes** (`OAuthCredential`, not internal type aliases) with `toJSON()` and `static fromJSON()` where applicable.

- `OAuthCredential.rawNonce` — used for Sign in with Apple and Facebook limited-login flows (matches firebase-js-sdk credential options). OAuth 1.0 token secrets (e.g. Twitter) use the inherited `AuthCredential.secret` bridge field instead of `rawNonce`.
- RN Firebase credentials retain internal `token` / `secret` bridge fields required by the native modules (implementation detail; omitted from generated API reference).

- `OAuthProvider.credentialFromResult` / `credentialFromError` and sibling provider helpers (`GoogleAuthProvider`, `GithubAuthProvider`, `TwitterAuthProvider`, `FacebookAuthProvider`, `PhoneAuthProvider`) **always return `null` at runtime today**. Declared types match firebase-js-sdk.
  - **iOS/Android:** no native extraction planned — credentials are not recoverable from native provider results.
  - **Other/Hermes:** not delegated (firebase-js-sdk credential recovery is tied to popup/redirect flows).
  - **Other/Web:** future implementation should delegate to firebase-js-sdk in the auth web bridge — do not invest in native iOS/Android bridge work for this.
- `GoogleAuthProvider.credential()` throws when both `idToken` and `accessToken` are absent (matching firebase-js-sdk).
- `FacebookAuthProvider.credential(token)` matches firebase-js-sdk. React Native Firebase also exports `credential(token, secret)` for Facebook limited-login nonce behavior — an intentional extension documented in compare:types.

## Multi-factor

- Modular `multiFactor(user)` now uses the user's auth instance instead of always calling `getAuth()`, fixing secondary Firebase app usage.
- Namespaced `firebase.auth().multiFactor(user)` now correctly validates that `user` is the `currentUser`.
- `TotpSecret.generateQrCodeUrl()` returns `Promise<string>` on iOS/Android (native bridge). firebase-js-sdk returns a synchronous string.
- `TotpSecret.openInOtpApp(qrCodeUrl)` — RN-only helper that deep-links into a One-Time Password authenticator app; not part of firebase-js-sdk.
- **Other platforms:** MFA and TOTP flows are exercised in `tests/local-tests` via the firebase-js-sdk bridge — not a gap to “port” from native overloads.

## Normalized modular return values

Modular helpers normalize several return shapes toward firebase-js-sdk:

- `UserCredential` includes top-level `providerId` and `operationType`.
- When the native bridge returns federated metadata, `additionalUserInfo` is attached as an **enumerable** property on modular `UserCredential` objects. Core fields match firebase-js-sdk (`isNewUser`, `profile`, `providerId`, `username`); extra native keys are copied onto the object for backwards compatibility.
- Use `getAdditionalUserInfo(userCredential)` for the canonical read (returns `AdditionalUserInfo | null`, same shape as firebase-js-sdk). For TypeScript when you need provider-specific native extras, cast to `AdditionalUserInfoNative` (`AdditionalUserInfo & Record<string, unknown>`).
- `checkActionCode` normalizes `fromEmail` to `previousEmail` and coerces multi-factor info shapes.
- `signInWithPhoneNumber` wraps confirmation results and validates `verificationId` presence.

## `initializeAuth`

`initializeAuth(app, deps?)` accepts the firebase-js-sdk `Dependencies` type for API parity, but persistence, popup redirect resolver, and error-map dependencies are ignored because native SDKs manage auth state.

## Namespaced type adjustments

- `FirebaseAuthTypes.UserInfo` profile fields can be null.
- `firebase.auth().config` is typed as `Record<string, never>` on the namespaced API (stricter than modular `auth.config`).

# Cloud Messaging

**PR:** [#9053](https://github.com/invertase/react-native-firebase/pull/9053)

Notification **permission** APIs in `@react-native-firebase/messaging` are **deprecated** in v25. They are not Firebase-specific; dedicated libraries handle permissions more completely.

| Deprecated API                                   | Use instead                                                                                                                                                                  |
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `requestPermission(messaging, …)`                | [`react-native-permissions`](https://github.com/zoontek/react-native-permissions) or [`expo-notifications`](https://docs.expo.dev/versions/latest/sdk/notifications/) (Expo) |
| `hasPermission(messaging)`                       | Same                                                                                                                                                                         |
| `registerDeviceForRemoteMessages(messaging)`     | Same (platform setup)                                                                                                                                                        |
| `isDeviceRegisteredForRemoteMessages(messaging)` | Same                                                                                                                                                                         |
| `AuthorizationStatus` static on messaging        | Permission library equivalents                                                                                                                                               |

These APIs still work at runtime but are marked `@deprecated` in TypeScript and documented for removal in a future major release. See [#6283](https://github.com/invertase/react-native-firebase/issues/6283).

# Automated migration checklist

Use this section when running scripted or agent-assisted upgrades from v24 → v25.

## 1. Upgrade dependencies

```bash
# Bump all @react-native-firebase/* packages to v25 together (monorepo versions are aligned)
yarn add @react-native-firebase/app@^25.0.0 …
cd ios && pod install
```

Ensure **Xcode 26.2+** for iOS builds.

## 2. Fix TypeScript by package

For each `@react-native-firebase/<pkg>` in your imports:

| Package         | Search for                                                        | Replace with                                                                |
| --------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------------- |
| `storage`       | `refFromURL`, `child(`, `FirebaseStorageTypes`, `.statics.`       | `ref()`, root imports, modular functions                                    |
| `database`      | `ServerValue`, `.orderByChild(`, `await goOffline`                | `serverTimestamp()`, `query`/`orderByChild` functions, sync `goOffline`     |
| `remote-config` | `fetch(`, `setDefaults`, `setConfigSettings`, `.value`, `.source` | `fetchConfig`, `defaultConfig`/`settings` props, `asString()`/`getSource()` |
| `perf`          | `.newTrace`, `.newHttpMetric`, `await initializePerformance`      | `trace()`, `httpMetric()`, sync `initializePerformance`                     |
| `installations` | `.getId()`, `.getToken()`, `.delete()`                            | `getId()`, `getToken()`, `deleteInstallations()`                            |
| `app-check`     | `appCheck().getToken()`, value import of `FirebaseAppCheckTypes`  | `initializeAppCheck` + `getToken()`, `import type`                          |
| `auth`          | `FirebaseAuthTypes`, `AppleAuthProvider`, `OIDCAuthProvider`      | Root modular types, `OAuthProvider`                                         |
| `messaging`     | `requestPermission`, `hasPermission`                              | `react-native-permissions` / `expo-notifications`                           |

## 3. Validate

```bash
yarn compile          # root TypeScript compile
yarn compare:types    # maintainers: per-package drift vs firebase-js-sdk
yarn tests:jest packages/<pkg>/__tests__   # targeted tests for touched packages
```

## 4. Namespaced API users

If you only use `firebase.storage()`, `firebase.auth()`, etc. and do not import modular types:

- You may see **deprecation warnings** in IDE / `@deprecated` JSDoc.
- Plan a gradual move to modular imports; namespaced removal is planned for a future major release.
- Runtime behavior should remain compatible for most flows.

## Related PRs (v25 breaking changes since v24.0.0)

| Package                  | PR / commit                                                           |
| ------------------------ | --------------------------------------------------------------------- |
| Storage                  | [#8824](https://github.com/invertase/react-native-firebase/pull/8824) |
| App Check                | [#8889](https://github.com/invertase/react-native-firebase/pull/8889) |
| Remote Config            | [#8972](https://github.com/invertase/react-native-firebase/pull/8972) |
| Realtime Database        | [#8977](https://github.com/invertase/react-native-firebase/pull/8977) |
| Performance              | `4aedfe883`                                                           |
| Installations            | `739a4ca36`                                                           |
| Auth                     | [#8991](https://github.com/invertase/react-native-firebase/pull/8991) |
| Messaging (deprecations) | [#9053](https://github.com/invertase/react-native-firebase/pull/9053) |
| Native SDKs (Xcode)      | `c8c1fc105`                                                           |

Packages migrated to TypeScript **without** public API breaks in v25: `@react-native-firebase/app-distribution` ([#8967](https://github.com/invertase/react-native-firebase/pull/8967)), `@react-native-firebase/ml` ([#9005](https://github.com/invertase/react-native-firebase/pull/9005)).
```

### Migrating to v26

Source: https://rnfirebase.io/migrating-to-v26

```mdx

Version 26 removes the deprecated **namespaced** JavaScript API from selected packages. Each migrated package is **modular-only**: use `getX(app?)` and root-level helper functions from the package entry point. Namespaced default exports, `firebase.<module>()`, and `Firebase*Types` namespaces are removed from those packages.

If you are upgrading from v24 or earlier, complete [Migrating to v25](/migrating-to-v25) first — v25 TypeScript alignment changes still apply.

## Table of contents

- [New Architecture requirement](/migrating-to-v26#new-architecture-requirement)
- [Platform behavior differences](/migrating-to-v26#platform-behavior-differences)
- [firebase-js-sdk API parity improvements](/migrating-to-v26#firebase-js-sdk-api-parity-improvements)
- [General pattern](/migrating-to-v26#general-pattern)
- [App](/migrating-to-v26#app)
- [Machine Learning (ML)](/migrating-to-v26#machine-learning-ml)
- [In-App Messaging](/migrating-to-v26#in-app-messaging)
- [Installations](/migrating-to-v26#installations)
- [Cloud Messaging](/migrating-to-v26#cloud-messaging)
  - [iOS APNs registration (ARM64 Simulator + new Promise rejections)](/migrating-to-v26#ios-apns-registration-arm64-simulator--new-promise-rejections)
- [App Distribution](/migrating-to-v26#app-distribution)
- [Cloud Functions](/migrating-to-v26#cloud-functions)
- [Performance Monitoring](/migrating-to-v26#performance-monitoring)
- [App Check](/migrating-to-v26#app-check)
- [Analytics](/migrating-to-v26#analytics)
- [Remote Config](/migrating-to-v26#remote-config)
- [Crashlytics](/migrating-to-v26#crashlytics)
- [Realtime Database](/migrating-to-v26#realtime-database)
- [Cloud Storage](/migrating-to-v26#cloud-storage)
- [Authentication](/migrating-to-v26#authentication)
- [Cloud Firestore](/migrating-to-v26#cloud-firestore)
- [Automated migration checklist](/migrating-to-v26#automated-migration-checklist)

## New Architecture requirement

From v26 onward, **every React Native Firebase package with a native bridge** is implemented as a **Codegen TurboModule** and **requires React Native's New Architecture**. The legacy bridge modules were removed in the coordinated v26 break.

If you cannot enable New Architecture yet, **stay on v25** (or v23 if you still depend on legacy-architecture Cloud Functions — see below).

### Exceptions (no New Architecture requirement)

| Package                           | Why                                                                                  |
| --------------------------------- | ------------------------------------------------------------------------------------ |
| `@react-native-firebase/ai`       | Pure JavaScript — uses the firebase-js-sdk web interop layer; no native TurboModule. |
| `@react-native-firebase/vertexai` | Pure JavaScript — same as `ai`; deprecated in favor of `@react-native-firebase/ai`.  |

### Cloud Functions (already required since v24)

`@react-native-firebase/functions` has required New Architecture **since v24** ([Migrating to v24 — Cloud Functions](/migrating-to-v24#cloud-functions)). v26 extends the same requirement to all remaining native modules.

### Enable New Architecture

**React Native CLI (Android)** — set in `android/gradle.properties`:

```properties
newArchEnabled=true
```

**React Native CLI (iOS)** — New Architecture is enabled when `RCT_NEW_ARCH_ENABLED=1` during `pod install` (React Native sets this from `newArchEnabled` on recent templates). Follow the [React Native New Architecture guide](https://reactnative.dev/docs/the-new-architecture/landing-page) for your RN version.

**Expo** — enable in your app config (Expo SDK 52+):

```json
{
  "expo": {
    "newArchEnabled": true
  }
}
```

Rebuild native projects after changing this (`pod install`, clean Android build).

### If New Architecture is disabled

Native builds fail early with explicit guards:

- **Android:** Gradle scripts print `New Architecture support is required for @react-native-firebase/<package>` and fail the build.
- **iOS:** `pod install` fails with `<PodName> requires New Architecture. Enable New Architecture to use this module`.

### Platform behavior differences

When migrating from firebase-js-sdk web examples, watch for these high-impact runtime divergences:

{/* prettier-ignore */}
| Area | firebase-js-sdk (web) | React Native Firebase |
| --- | --- | --- |
| **Auth** | Browser persistence, redirects, reCAPTCHA, `revokeAccessToken`, `useDeviceLanguage`, `linkWithPhoneNumber` / `reauthenticateWithPhoneNumber` | Native iOS/Android SDKs manage persistence. Several web-only helpers **throw synchronously** on React Native (`setPersistence`, `getRedirectResult`, `revokeAccessToken`, `useDeviceLanguage`, `linkWithPhoneNumber`, `reauthenticateWithPhoneNumber`). Use native provider flows instead. |
| **Analytics** | Cross-platform modular API | On-device conversion measurement helpers (`initiateOnDeviceConversionMeasurement*`) are **iOS only**; on Android and web the JS layer resolves without calling native (no-op). `logTransaction` (verified iOS in-app purchase events) is **iOS only** and **rejects** on other platforms. |
| **Realtime Database** | `goOnline` / `goOffline` are synchronous; web transport toggles | `goOnline` / `goOffline` are **synchronous** through TurboModules, matching firebase-js-sdk. `getServerTime`, `setPersistenceEnabled`, and `setPersistenceCacheSizeBytes` are RN-specific. `forceLongPolling` / `forceWebSockets` **throw** — transport is native-controlled. |
| **Phone Number Verification** | N/A (Android SDK only) | **Android only** — all entry points throw on iOS and Web. |
| **Cloud Storage** | `uploadBytes` / `uploadString` from JS blobs; upload task controls return sync `boolean` | `putFile` and `writeToFile` are **native-only** file-path APIs. `UploadTask.pause()` / `.resume()` / `.cancel()` also return sync `boolean` values through native TurboModules. |
| **Cloud Messaging** | Web push / service-worker surface | FCM token lifecycle, permissions, background handlers, and **APNs token APIs are iOS only**. Event delivery still uses the legacy native event proxy during the TurboModule migration; behavior matches pre-v26 releases but will change when Codegen events land in a future release. |
| **Remote Config** | `reset()` clears server and local state | `reset()` is **Android only** — iOS does not clear activated, fetched, or default Remote Config values. `setDefaultsFromResource` loads from native resource files (`.plist` / XML). |
| **Performance** | `initializePerformance` returns synchronously; trace `start`/`stop` are sync | `initializePerformance` is **synchronous** on RN (applies settings to the native instance). Trace, HTTP metric, and screen trace `start`/`stop` are **synchronous** through TurboModules, matching firebase-js-sdk. |
| **Firestore** | IndexedDB persistence, `memoryLocalCache`, `persistentLocalCache`, FieldValue `maximum()` / `minimum()` sentinels | Local cache factories and IndexedDB APIs are **web only**. Persistence is controlled by the native Firestore SDK. `initializeFirestore` returns **`Firestore` synchronously**. FieldValue `maximum()` / `minimum()` are not exported yet because iOS SDK 12.15.0 lacks matching native factories. |
| **App Check** | reCAPTCHA version 3 / Enterprise web providers | Use `ReactNativeFirebaseAppCheckProvider` for Device Check, App Attest, Play Integrity, etc. Web reCAPTCHA provider classes have **no RN equivalent**. |
| **RN-native-only modules** | No web SDK | `crashlytics`, `in-app-messaging`, `app-distribution`, and `ml` are **React Native only** — no firebase-js-sdk modular surface. |

Per-package notes also appear in each module's [usage](/) page under **Platform support**.

## firebase-js-sdk API parity improvements

v26 aligns React Native Firebase modular types and several runtime signatures with **firebase-js-sdk** where TurboModules removed bridge-forced `Promise` wrappers. Use this section when porting web SDK examples or when scripted upgrades hit TypeScript or runtime errors.

**Migration rule:** If a method below is **sync**, remove `await` and any `.then()` / `.catch()` chain on its return value — sync APIs return `void` or a value directly; calling `.then()` on `void` throws at runtime.

### Synchronous APIs enabled by TurboModules

{/* prettier-ignore */}
| Method | Before (pre-v26 / legacy bridge) | After (v26) | Notes |
| ------ | -------------------------------- | ----------- | ----- |
| `registerVersion(library, version, variant?)` | `Promise<void>` (web) | `void` — throws on RN | Web-only on RN; remove `await` if copied from web samples |
| `isSignInWithEmailLink(auth, emailLink)` | `Promise<boolean>` | `boolean` | Remove `await` |
| `TotpSecret.generateQrCodeUrl(accountName?, issuer?)` | `Promise<string>` | `string` | Remove `await`; omitted args default from current user / app name |
| `trace(…).start()` / `.stop()` | `Promise<null>` | `void` | Remove `await` on perf trace lifecycle |
| `httpMetric(…).start()` / `.stop()` | `Promise<null>` | `void` | Same as traces |
| `ScreenTrace.start()` / `.stop()` | `Promise<null>` | `void` | Same as traces |
| `startScreenTrace(performance, screenName)` | `Promise<ScreenTrace>` | `ScreenTrace` | Remove `await` |
| `goOnline(db)` / `goOffline(db)` | `Promise<void>` | `void` | Remove `await` |
| `UploadTask.pause()` / `.resume()` / `.cancel()` | `Promise<boolean>` | `boolean` | Remove `await`; use the returned boolean directly |

### Type parity improvements

{/* prettier-ignore */}
| Export / method | Before | After | Notes |
| --------------- | ------ | ----- | ----- |
| `onSnapshotsInSync(firestore, observer)` | `next?: () => void`; `error?: Error` | `next?: (value: void) => void`; `error?: FirestoreError` | Callback overload `onSnapshotsInSync(firestore, () => {})` unchanged |
| `uploadBytes(ref, data, metadata?)` | Return type `TaskResult` | `UploadResult` | Alias rename only |
| `uploadBytesResumable(ref, data, metadata?)` | Return type `Task` | `UploadTask` | Alias rename only |
| Storage / Remote Config error callbacks | `NativeFirebaseError` | `FirebaseError` / `StorageError` | `NativeFirebaseError` remains assignable to `FirebaseError` |
| `AppCheckTokenListener` | Not exported | Exported type alias | Matches firebase-js-sdk observer typing |
| `FunctionsError`, `FunctionsErrorCodeCore` | Missing or misaligned | Exported / aligned | Registry + compare-types config |
| `firestore/pipelines` (`StageOptions`, `TimeGranularity`, `isType`, …) | Declaration drift | Aligned to SDK `.d.ts` | Type-only; no call-shape change |

### Synchronous modular return values

{/* prettier-ignore */}
| Method | Before | After | Notes |
| ------ | ------ | ----- | ----- |
| `logEvent(analytics, …)` | `Promise<void>` | `void` | Fire-and-forget; remove `await` / `.then()` |
| `initializeAppCheck(app?, options?)` | `Promise<AppCheck>` | `AppCheck` | Returns handle immediately; provider setup continues natively |
| `initializeFirestore(app, settings, databaseId?)` | `Promise<Firestore>` | `Firestore` | Returns instance immediately; `settings()` runs on native bridge |
| `getRemoteConfig(app?, options?)` | `(app?)` only | `(app?, options?: RemoteConfigOptions)` | `options` accepted for parity; no-op on native |
| `aggregateFieldEqual()` | Not exported | Exported | Aggregate query helper |
| `LastFetchStatus` literals | — | — | **Unchanged** — native still uses `no_fetch_yet` / `throttled` (documented drift) |

### Firestore parity additions

{/* prettier-ignore */}
| Export / method | Before | After | Notes |
| --------------- | ------ | ----- | ----- |
| `TransactionOptions` | Not exported | Exported | Type for transaction retry options |
| `runTransaction(firestore, updateFunction, options?)` | No options argument | Accepts `options?: TransactionOptions` | `{ maxAttempts?: number }` on native iOS and Android; omitted options use the SDK default of **5** retries; explicit values **&lt; 1** throw `Max attempts must be at least 1` (firebase-js-sdk parity) |
| `aggregateQuerySnapshotEqual()` | Not exported | Exported | Aggregate query snapshot equality helper, alongside `aggregateFieldEqual()` |
| `maximum(n)` / `minimum(n)` FieldValue sentinels | Exported by firebase-js-sdk 12.15.0 | Not exported by RNFB yet | Android SDK 34.15.0 has native factories, but iOS Firebase Firestore SDK 12.15.0 does not; RNFB waits for cross-platform parity |

### Observer and Promise pitfalls

{/* prettier-ignore */}
| Pitfall | Symptom | Fix |
| ------- | ------- | --- |
| `.then()` on sync API | `TypeError: … .then is not a function` | Use direct call: `logEvent(…)` not `logEvent(…).then(…)` |
| `await` on sync API | Unnecessary microtask delay; misleading types in strict TS | Drop `await` where the table above marks the API sync |
| `onConfigUpdate(remoteConfig, observer)` | Runtime throw: observer must include **both** `next` and `error` functions | Pass `{ next: (update) => {…}, error: (err) => {…} }` — do not omit `error` |
| `onSnapshotsInSync` observer form | Type error on `error` callback | Type `error` as `(error: FirestoreError) => void`, or use the `() => void` callback overload |
| `runTransaction(..., { maxAttempts: 0 })` or negative | Runtime throw: `Max attempts must be at least 1` | Omit `options` for the default **5** attempts, or pass `maxAttempts: 1` or higher |

## General pattern

For each package listed in this guide:

1. Remove default export / `firebase.<module>()` usage — import modular helpers from the package root.
2. Replace `Firebase*Types` namespace imports with root types (`InAppMessaging`, `Installations`, `FirebaseML`, etc.).
3. Prefer free functions (`getId(installations)`, `setMessagesDisplaySuppressed(inAppMessaging, enabled)`) over instance methods on service objects.

**Modular-only in v26:** `@react-native-firebase/app`, `@react-native-firebase/ml`, `@react-native-firebase/in-app-messaging`, `@react-native-firebase/installations`, `@react-native-firebase/messaging`, `@react-native-firebase/app-distribution`, `@react-native-firebase/functions`, `@react-native-firebase/perf`, `@react-native-firebase/app-check`, `@react-native-firebase/remote-config`, `@react-native-firebase/crashlytics`, `@react-native-firebase/database`, `@react-native-firebase/storage`, `@react-native-firebase/analytics`, `@react-native-firebase/auth`, and `@react-native-firebase/firestore`. Other packages may still expose the namespaced API with deprecation warnings; modular imports remain the supported path.

# App

The namespaced API for `@react-native-firebase/app` has been **removed**. This package is **modular-only** — use named exports such as `getApp()`, `initializeApp()`, and `getUtils()`.

## Removed namespaced API

| Removed                                               | Replacement (modular)                                               |
| ----------------------------------------------------- | ------------------------------------------------------------------- |
| Default export `firebase`                             | Named exports from `@react-native-firebase/app`                     |
| `firebase.app(name?)`                                 | `getApp(name?)`                                                     |
| `firebase.apps`                                       | `getApps()`                                                         |
| `firebase.initializeApp(options, name?)`              | `initializeApp(options, name?)`                                     |
| `firebase.utils(app?)`                                | `getUtils(app?)`                                                    |
| `firebase.utils.FilePath`                             | `FilePath` (named export)                                           |
| `firebase.SDK_VERSION`                                | `SDK_VERSION` (named export)                                        |
| `firebase.setLogLevel(...)`                           | `setLogLevel(...)`                                                  |
| `firebase.<module>()` accessors on the default export | Import modular helpers from each `@react-native-firebase/*` package |

## Example

```js
// Previously (removed)
import firebase from '@react-native-firebase/app';

firebase.app().name;
firebase.initializeApp(options, 'secondary');
firebase.utils().FilePath.DOCUMENT_DIRECTORY;
```

```js
// Now
import { getApp, initializeApp, getUtils, FilePath } from '@react-native-firebase/app';

getApp().name;
initializeApp(options, 'secondary');
getUtils().FilePath.DOCUMENT_DIRECTORY;
// or
FilePath.DOCUMENT_DIRECTORY;
```

## Breaking: Android Play Services availability Promise behavior

On Android, `makePlayServicesAvailable()` now rejects when the Play Services availability task is canceled or fails. Previously, the call always resolved even when the update flow was not completed. Update callers to handle rejection:

```js
import { getUtils } from '@react-native-firebase/app';

try {
  await getUtils().makePlayServicesAvailable();
} catch (error) {
  // Handle canceled or failed Play Services update
}
```

# Machine Learning (ML)

The namespaced API for `@react-native-firebase/ml` has been **removed**. This package is **modular-only** — use `getML(app?)`.

## Removed namespaced API

| Removed                     | Replacement (modular)                 |
| --------------------------- | ------------------------------------- |
| `firebase.ml()`             | `getML()` or `getML(app)`             |
| Default export `ml()`       | `getML()` or `getML(app)`             |
| `FirebaseMLTypes` namespace | Import `FirebaseML` from package root |

## Example

```js
// Previously (removed)
import firebase from '@react-native-firebase/app';
import ml from '@react-native-firebase/ml';

firebase.ml().app.name;
ml().app.name;
```

```js
// Now
import { getApp } from '@react-native-firebase/app';
import { getML } from '@react-native-firebase/ml';

getML().app.name; // default app
getML(getApp('secondaryFromNative')).app.name;
```

# In-App Messaging

The namespaced API for `@react-native-firebase/in-app-messaging` has been **removed**. This package is **modular-only** — use `getInAppMessaging()` and the root-level helper functions.

There is no `firebase/in-app-messaging` entry in the firebase-js-sdk modular surface; React Native Firebase follows the same modular service-instance pattern used for other native-only modules.

## Removed namespaced API

| Removed                                     | Replacement (modular)                                                                |
| ------------------------------------------- | ------------------------------------------------------------------------------------ |
| `firebase.inAppMessaging()`                 | `getInAppMessaging()`                                                                |
| Default export `inAppMessaging()`           | `getInAppMessaging()`                                                                |
| `FirebaseInAppMessagingTypes`               | Import `InAppMessaging` and helpers (`setMessagesDisplaySuppressed`, etc.) from root |
| Instance-only usage without modular helpers | Prefer free functions: `setMessagesDisplaySuppressed(inAppMessaging, enabled)`, etc. |

## Example: suppressing messages during setup

```js
// Previously (removed)
import firebase from '@react-native-firebase/app';
import inAppMessaging from '@react-native-firebase/in-app-messaging';

await firebase.inAppMessaging().setMessagesDisplaySuppressed(true);
// or
await inAppMessaging().setMessagesDisplaySuppressed(true);
```

```js
// Now
import {
  getInAppMessaging,
  setMessagesDisplaySuppressed,
} from '@react-native-firebase/in-app-messaging';

await setMessagesDisplaySuppressed(getInAppMessaging(), true);
```

See [In-App Messaging usage](/in-app-messaging/usage) for additional examples.

# Installations

The namespaced API for `@react-native-firebase/installations` has been **removed**. This package is **modular-only** — use `getInstallations()` and the root-level helper functions.

Modular `getInstallations()` returns a firebase-js-sdk-shaped `Installations` object exposing only `app`. Use modular helper functions instead of instance methods.

## Removed namespaced API

| Removed                                               | Replacement (modular)                                                                     |
| ----------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `firebase.installations()`                            | `getInstallations()`                                                                      |
| Default export `installations()`                      | `getInstallations()`                                                                      |
| `FirebaseInstallationsTypes`                          | Import `Installations` and helpers (`getId`, `getToken`, `deleteInstallations`) from root |
| `installations.getId()` / `.getToken()` / `.delete()` | `getId(installations)`, `getToken(installations)`, `deleteInstallations(installations)`   |

## Breaking changes (modular call shape)

| Before                     | Now                                                             |
| -------------------------- | --------------------------------------------------------------- |
| `installations.getId()`    | `getId(installations)`                                          |
| `installations.getToken()` | `getToken(installations)`                                       |
| `installations.delete()`   | `deleteInstallations(installations)` — argument is **required** |

## Example

```js
// Previously (removed)
import firebase from '@react-native-firebase/app';
import installations from '@react-native-firebase/installations';

const id = await firebase.installations().getId();
// or
const id = await installations().getId();
```

```js
// Now
import { getInstallations, getId } from '@react-native-firebase/installations';

const installations = getInstallations();
const id = await getId(installations);
```

See [Installations usage](/installations/usage) for additional examples.

# Cloud Messaging

The namespaced API for `@react-native-firebase/messaging` has been **removed**. This package is **modular-only** — use `getMessaging()` and the root-level helper functions.

Modular `getMessaging()` returns a firebase-js-sdk-shaped `Messaging` object. Prefer free functions (`getToken(messaging)`, `onMessage(messaging, listener)`, etc.) over calling methods on the service instance.

## Removed namespaced API

| Removed                                                    | Replacement (modular)                                                         |
| ---------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `firebase.messaging()`                                     | `getMessaging()`                                                              |
| Default export `messaging()`                               | `getMessaging()`                                                              |
| `FirebaseMessagingTypes`                                   | Import `Messaging`, `RemoteMessage`, and helpers from package root            |
| `messaging.getToken()` / `.onMessage()` / etc.             | `getToken(messaging)`, `onMessage(messaging, listener)`, etc.                 |
| `messaging.setDeliveryMetricsExportToBigQuery()`           | `experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, enabled)` |
| `firebase.messaging.SDK_VERSION` / `messaging.SDK_VERSION` | `SDK_VERSION` top-level export from package root                              |

## Example: foreground messages

```js
// Previously (removed)
import firebase from '@react-native-firebase/app';
import messaging from '@react-native-firebase/messaging';

firebase.messaging().onMessage(message => {
  console.log(message.data);
});
// or
messaging().onMessage(message => {
  console.log(message.data);
});
```

```js
// Now
import { getMessaging, onMessage } from '@react-native-firebase/messaging';

onMessage(getMessaging(), message => {
  console.log(message.data);
});
```

## Example: delivery metrics to BigQuery

```js
// Previously (removed)
await firebase.messaging().setDeliveryMetricsExportToBigQuery(true);
```

```js
// Now
import {
  getMessaging,
  experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
} from '@react-native-firebase/messaging';

await experimentalSetDeliveryMetricsExportedToBigQueryEnabled(getMessaging(), true);
```

See [Cloud Messaging usage](/messaging/usage) for additional examples.

## iOS APNs registration (ARM64 Simulator + new Promise rejections)

On **ARM64 iOS Simulator** (Apple Silicon), React Native Firebase **no longer calls** UIKit
`registerForRemoteNotifications` from messaging **registration** and **auto-registration** paths
(including the TurboModule `registerDeviceForRemoteMessages` path, `requestPermission`'s APNs
side-effect, and native auto-register on app launch). Calling that UIKit API can block the main
thread indefinitely. **Physical iOS devices are unchanged** — they still call UIKit
`registerForRemoteNotifications` as before.

This is intentional product policy so the main thread stays responsive on Simulator. You will
**not** receive a real APNs device token on ARM64 Simulator; use a physical device for end-to-end
push registration and delivery.

### New possible Promise rejections

Prior releases could hang forever waiting for APNs registration (especially when the main thread
was wedged) and did **not** reject with the codes below in the same situations. Apps that assumed
`registerDeviceForRemoteMessages` always resolved (or never rejected) must handle these errors.

#### `messaging/registration-timeout`

- **When:** After about **10 seconds**, a timer on a **global** queue (not the main queue) rejects
  the in-flight registration Promise when APNs registration has not settled.
- **APIs:** Surfaces on **`registerDeviceForRemoteMessages`** only.
- **`requestPermission`:** On ARM64 Simulator, the APNs UIKit side-effect is **skipped**; the
  permission Promise still resolves with `AuthorizationStatus` and does **not** reject with this
  code.
- **ARM64 Simulator:** Expected when you call `registerDeviceForRemoteMessages` — UIKit register is
  skipped, the Promise stays pending, then the global-queue timer rejects.
- **Physical device:** Possible if the system never delivers a registration success/failure
  callback within about 10 seconds (for example missing notification permission, or a blocked main
  thread).
- **Code / message:** `messaging/registration-timeout` —
  `registerDeviceForRemoteMessages timed out waiting for APNs device registration. The system did not respond — possibly missing notification permission, or the main thread is blocked (common on ARM64 Simulator).`

#### `messaging/registration-superseded`

- **When:** A newer `registerDeviceForRemoteMessages` call starts while a prior attempt is still in
  flight; the earlier Promise is rejected so it is not orphaned.
- **APIs:** Surfaces on **`registerDeviceForRemoteMessages`** only (overlapping calls).
- **Code / message:** `messaging/registration-superseded` —
  `registerDeviceForRemoteMessages was called again before the previous APNs registration attempt settled.`

Further usage notes: [Auto Registration (iOS)](/messaging/usage#auto-registration-ios),
[iOS Messaging Setup](/messaging/usage/ios-setup). Maintainer platform detail:
[iOS APNs registration on Simulator](https://github.com/invertase/react-native-firebase/blob/main/okf-bundle/packages/messaging/ios-apns-simulator-registration.md).

# App Distribution

The namespaced API for `@react-native-firebase/app-distribution` has been **removed**. This package is **modular-only** — use `getAppDistribution()` and the root-level helper functions.

## Removed namespaced API

| Removed                                     | Replacement (modular)                                                                                                                     |
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `firebase.appDistribution()`                | `getAppDistribution()`                                                                                                                    |
| Default export `appDistribution()`          | `getAppDistribution()`                                                                                                                    |
| `FirebaseAppDistributionTypes`              | Import `AppDistribution`, `AppDistributionRelease` from package root                                                                      |
| `appDistribution.isTesterSignedIn()` / etc. | `isTesterSignedIn(appDistribution)`, `signInTester(appDistribution)`, `checkForUpdate(appDistribution)`, `signOutTester(appDistribution)` |
| `firebase.appDistribution.SDK_VERSION`      | `SDK_VERSION` top-level export from package root                                                                                          |

## Example

```js
// Previously (removed)
import firebase from '@react-native-firebase/app';
import appDistribution from '@react-native-firebase/app-distribution';

await firebase.appDistribution().isTesterSignedIn();
```

```js
// Now
import { getAppDistribution, isTesterSignedIn } from '@react-native-firebase/app-distribution';

const appDistribution = getAppDistribution();
await isTesterSignedIn(appDistribution);
```

# Cloud Functions

The namespaced API for `@react-native-firebase/functions` has been **removed**. This package is **modular-only** — use `getFunctions()` and root-level helper functions.

## Removed namespaced API

| Removed                       | Replacement (modular)                                                         |
| ----------------------------- | ----------------------------------------------------------------------------- |
| `firebase.functions()`        | `getFunctions()` or `getFunctions(app, regionOrCustomDomain)`                 |
| Default export `functions()`  | `getFunctions()`                                                              |
| `FirebaseFunctionsTypes`      | Import `Functions`, `HttpsCallable`, `HttpsErrorCode`, etc. from package root |
| `functions().httpsCallable()` | `httpsCallable(functions, name, options?)`                                    |
| `functions().useEmulator()`   | `connectFunctionsEmulator(functions, host, port)`                             |

## Example

```js
// Previously (removed)
import firebase from '@react-native-firebase/app';
import functions from '@react-native-firebase/functions';

await firebase.functions().httpsCallable('myFn')({ foo: 'bar' });
```

```js
// Now
import { getFunctions, httpsCallable } from '@react-native-firebase/functions';

const functions = getFunctions();
await httpsCallable(functions, 'myFn')({ foo: 'bar' });
```

# Performance Monitoring

The namespaced API for `@react-native-firebase/perf` has been **removed**. This package is **modular-only** — use `getPerformance()` and root-level helper functions.

## Removed namespaced API

| Removed                    | Replacement (modular)                                                    |
| -------------------------- | ------------------------------------------------------------------------ |
| `firebase.perf()`          | `getPerformance()`                                                       |
| Default export `perf()`    | `getPerformance()`                                                       |
| `FirebasePerformanceTypes` | Import `FirebasePerformance`, `PerformanceTrace`, etc. from package root |
| `perf().newTrace()`        | `trace(performance, name)`                                               |
| `perf().newHttpMetric()`   | `httpMetric(performance, url, httpMethod)`                               |

## Signature changes

- `Trace`, `HttpMetric`, and `ScreenTrace` `start()` / `stop()` now return `void` synchronously (previously `Promise<null>`), matching the firebase-js-sdk web `PerformanceTrace`. The underlying native calls are in-memory, so no `await` is required — remove any `await` on these calls.
- `startScreenTrace(performance, screenName)` and the internal `startTrace(name)` helper now return the trace instance synchronously instead of a `Promise`.

# App Check

The namespaced API for `@react-native-firebase/app-check` has been **removed**. This package is **modular-only** — use `getAppCheck()` / `initializeAppCheck()` and root-level helper functions.

## Removed namespaced API

| Removed                     | Replacement (modular)                                            |
| --------------------------- | ---------------------------------------------------------------- |
| `firebase.appCheck()`       | `getAppCheck()` or `initializeAppCheck(app, options)`            |
| Default export `appCheck()` | `getAppCheck()`                                                  |
| `FirebaseAppCheckTypes`     | Import `AppCheck`, `AppCheckTokenResult`, etc. from package root |
| `appCheck().getToken()`     | `getToken(appCheck, forceRefresh?)`                              |

## Signature changes

- `initializeAppCheck(app?, options?)` returns `AppCheck` synchronously (previously `Promise<AppCheck>`). Native provider setup continues in the background — remove `await` and do not chain `.then()`.

# Analytics

The namespaced API for `@react-native-firebase/analytics` has been **removed**. This package is **modular-only** — use `getAnalytics()` and root-level helper functions.

Analytics supports only the **default Firebase app** (same as before). Calling `getAnalytics(secondaryApp)` throws.

## Removed namespaced API

| Removed                                                      | Replacement (modular)                                                                  |
| ------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
| `firebase.analytics()`                                       | `getAnalytics()`                                                                       |
| Default export `analytics()`                                 | `getAnalytics()`                                                                       |
| `FirebaseAnalyticsTypes`                                     | Import `Analytics`, event parameter types, etc. from package root                      |
| `analytics().logEvent(...)`                                  | `logEvent(analytics, ...)`                                                             |
| `analytics().setUserId(...)` etc.                            | `setUserId(analytics, ...)`, `setConsent(analytics, ...)`, etc.                        |
| Deprecated helper events (`logScreenView`, `logPurchase`, …) | Still exported as modular helpers; prefer `logEvent(analytics, 'screen_view', params)` |

## Signature changes

- `logEvent(analytics, …)` returns `void` synchronously (previously `Promise<void>`). Remove `await` and `.then()` chains.

## Example

```js
// Previously (removed)
import firebase from '@react-native-firebase/app';
import analytics from '@react-native-firebase/analytics';

await firebase.analytics().logEvent('screen_view', { screen_name: 'Home' });
await analytics().setUserId('user-123');
```

```js
// Now
import { getAnalytics, logEvent, setUserId } from '@react-native-firebase/analytics';

const analytics = getAnalytics();
logEvent(analytics, 'screen_view', { screen_name: 'Home' });
await setUserId(analytics, 'user-123');
```

See [Analytics usage](/analytics/usage) for additional examples.

# Remote Config

The namespaced API for `@react-native-firebase/remote-config` has been **removed**. This package is **modular-only** — use `getRemoteConfig()` and root-level helper functions.

Modular `getRemoteConfig()` returns a firebase-js-sdk-shaped `RemoteConfig` object with `app`, `settings`, `defaultConfig`, `fetchTimeMillis`, and `lastFetchStatus`. Use modular helper functions for fetch/activate/getters; assign `remoteConfig.settings` and `remoteConfig.defaultConfig` instead of removed modular `setConfigSettings()` / `setDefaults()` helpers.

## Removed namespaced API

| Removed                                                      | Replacement (modular)                                                                      |
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------ |
| `firebase.remoteConfig()`                                    | `getRemoteConfig()`                                                                        |
| Default export `remoteConfig()`                              | `getRemoteConfig()`                                                                        |
| `FirebaseRemoteConfigTypes`                                  | Import `RemoteConfig`, `Value`, etc. from package root                                     |
| `remoteConfig().activate()` etc.                             | `activate(remoteConfig)`, `fetchConfig(remoteConfig)`, `getValue(remoteConfig, key)`, etc. |
| `remoteConfig.LastFetchStatus` / `.ValueSource` on namespace | `LastFetchStatus`, `ValueSource` named exports                                             |

## Breaking changes (modular call shape)

| Before                                      | Now                                      |
| ------------------------------------------- | ---------------------------------------- |
| `remoteConfig().fetch()`                    | `fetchConfig(remoteConfig)`              |
| `remoteConfig().setConfigSettings({ ... })` | `remoteConfig.settings = { ... }`        |
| `remoteConfig().setDefaults({ ... })`       | `remoteConfig.defaultConfig = { ... }`   |
| `remoteConfig().onConfigUpdated(listener)`  | `onConfigUpdate(remoteConfig, observer)` |

## Example

```js
// Previously (removed)
import firebase from '@react-native-firebase/app';
import remoteConfig from '@react-native-firebase/remote-config';

await firebase.remoteConfig().fetchAndActivate();
const value = firebase.remoteConfig().getValue('key').asString();
```

```js
// Now
import { getRemoteConfig, fetchAndActivate, getValue } from '@react-native-firebase/remote-config';

const remoteConfig = getRemoteConfig();
await fetchAndActivate(remoteConfig);
const value = getValue(remoteConfig, 'key').asString();
```

See [Remote Config usage](/remote-config/usage) for additional examples.

# Crashlytics

The namespaced API for `@react-native-firebase/crashlytics` has been **removed**. This package is **modular-only** — use `getCrashlytics()` and root-level helper functions.

## Removed namespaced API

| Removed                        | Replacement (modular)                           |
| ------------------------------ | ----------------------------------------------- |
| `firebase.crashlytics()`       | `getCrashlytics()`                              |
| Default export `crashlytics()` | `getCrashlytics()`                              |
| `FirebaseCrashlyticsTypes`     | Import `Crashlytics` from package root          |
| `crashlytics().log()`          | `log(crashlytics, message)`                     |
| `crashlytics().recordError()`  | `recordError(crashlytics, error, jsErrorName?)` |

# Realtime Database

The namespaced API for `@react-native-firebase/database` has been **removed**. This package is **modular-only** — use `getDatabase()` and root-level helper functions.

## Removed namespaced API

| Removed                     | Replacement (modular)                                          |
| --------------------------- | -------------------------------------------------------------- |
| `firebase.database()`       | `getDatabase()` or `getDatabase(app, url?)`                    |
| Default export `database()` | `getDatabase()`                                                |
| `FirebaseDatabaseTypes`     | Import `Database`, `DatabaseReference`, etc. from package root |
| `database().ref()`          | `ref(database, path?)`                                         |
| `database().refFromURL()`   | `refFromURL(database, url)`                                    |

## Signature changes

- `goOnline(db)` and `goOffline(db)` (and the database instance methods) now return `void` synchronously (previously `Promise<void>`), matching firebase-js-sdk. Remove any `await` on these calls.

# Cloud Storage

The namespaced API for `@react-native-firebase/storage` has been **removed**. This package is **modular-only** — use `getStorage()` and root-level helper functions.

## Removed namespaced API

| Removed                    | Replacement (modular)                                                |
| -------------------------- | -------------------------------------------------------------------- |
| `firebase.storage()`       | `getStorage()` or `getStorage(app, bucketUrl?)`                      |
| Default export `storage()` | `getStorage()`                                                       |
| `FirebaseStorageTypes`     | Import `FirebaseStorage`, `StorageReference`, etc. from package root |
| `storage().ref()`          | `ref(storage, path?)`                                                |
| `storageRef.put()`         | `uploadBytesResumable(storageRef, data, metadata?)`                  |

## Signature changes

- `UploadTask.pause()`, `UploadTask.resume()`, and `UploadTask.cancel()` now return `boolean` synchronously (previously `Promise<boolean>`), matching firebase-js-sdk task semantics. Remove `await`, `.then()`, and `.catch()` from these control calls and use the returned boolean directly.
- `cancel()` delegates to the native SDK. Android cancels active uploads; iOS active-upload cancel follows the current Firebase Storage iOS SDK 12.15.0 behavior tracked in [firebase-ios-sdk#16353](https://github.com/firebase/firebase-ios-sdk/issues/16353).

# Authentication

The namespaced API for `@react-native-firebase/auth` has been **removed**. This package is **modular-only** — use `getAuth()` / `initializeAuth()` and root-level helper functions.

## Removed namespaced API

| Removed                                  | Replacement (modular)                                           |
| ---------------------------------------- | --------------------------------------------------------------- |
| `firebase.auth()`                        | `getAuth()` or `getAuth(app)`                                   |
| Default export `auth()`                  | `getAuth()`                                                     |
| `FirebaseAuthTypes` namespace            | Import `Auth`, `User`, `UserCredential`, etc. from package root |
| `auth().signInWithEmailAndPassword(...)` | `signInWithEmailAndPassword(getAuth(), ...)`                    |
| `auth().useEmulator(url)`                | `connectAuthEmulator(getAuth(), url, options?)`                 |
| `firebase.auth.EmailAuthProvider`        | `EmailAuthProvider` (named export)                              |

## Signature changes

- `isSignInWithEmailLink(auth, emailLink)` now returns `boolean` synchronously, matching firebase-js-sdk.
- `TotpSecret.generateQrCodeUrl(accountName?, issuer?)` now returns `string` synchronously, matching firebase-js-sdk. When `accountName` or `issuer` is omitted, React Native Firebase fills the same defaults from the current user email and Firebase app name.

## Example

```js
// Previously (removed)
import firebase from '@react-native-firebase/app';
import auth from '@react-native-firebase/auth';

firebase.auth().signOut();
auth().currentUser;
```

```js
// Now
import { getAuth, signOut } from '@react-native-firebase/auth';

const auth = getAuth();
await signOut(auth);
auth.currentUser;
```

# Cloud Firestore

The namespaced API for `@react-native-firebase/firestore` has been **removed**. This package is **modular-only** — use `getFirestore()` / `initializeFirestore()` and root-level helper functions. The `@react-native-firebase/firestore/pipelines` subpath export is unchanged.

## Removed namespaced API

| Removed                                        | Replacement (modular)                                                                 |
| ---------------------------------------------- | ------------------------------------------------------------------------------------- |
| `firebase.firestore()`                         | `getFirestore()` or `getFirestore(app, databaseId?)`                                  |
| Default export `firestore()`                   | `getFirestore()`                                                                      |
| `FirebaseFirestoreTypes` namespace             | Import `Firestore`, `DocumentReference`, `Query`, etc. from package root              |
| `firestore().collection(...)`                  | `collection(getFirestore(), ...)`                                                     |
| `firestore().doc(...)`                         | `doc(getFirestore(), ...)`                                                            |
| `firestore().batch()`                          | `writeBatch(getFirestore())`                                                          |
| `firestore().runTransaction(...)`              | `runTransaction(getFirestore(), updateFunction, options?)`                            |
| `firestore().useEmulator(...)`                 | `connectFirestoreEmulator(getFirestore(), ...)`                                       |
| `firestore().settings(...)`                    | `initializeFirestore(app, settings, databaseId?)`                                     |
| `firestore.FieldValue` / `firestore.Timestamp` | `FieldValue`, `Timestamp`, `FieldPath`, `Bytes`, `GeoPoint`, `Filter` (named exports) |
| `firestore.Blob`                               | `Bytes`                                                                               |

## Signature changes

- `initializeFirestore(app, settings, databaseId?)` returns `Firestore` synchronously (previously `Promise<Firestore>`). Remove `await` and `.then()`; settings apply on the native bridge after return.
- `TransactionOptions` is now exported. `runTransaction(firestore, updateFunction, options?)` accepts `{ maxAttempts?: number }` and passes the retry limit to the native iOS and Android Firestore SDKs. When `options` is omitted, the native default applies (**5** attempts, matching [firebase-js-sdk `runTransaction`](https://firebase.google.com/docs/reference/js/firestore_.md#runtransaction)). When `maxAttempts` is provided explicitly, values below **1** throw synchronously with `Max attempts must be at least 1` — the same validation as firebase-js-sdk.
- `aggregateFieldEqual()` and `aggregateQuerySnapshotEqual()` are now exported for aggregate query equality checks (firebase-js-sdk parity).
- FieldValue `maximum(n)` and `minimum(n)` sentinels from firebase-js-sdk 12.15.0 are not exported yet. Android Firebase Firestore SDK 34.15.0 supports them, but iOS Firebase Firestore SDK 12.15.0 does not expose matching `FIRFieldValue` factories, so React Native Firebase waits for cross-platform support.

## Example

```js
// Previously (removed)
import firebase from '@react-native-firebase/app';
import firestore from '@react-native-firebase/firestore';

firebase.firestore().collection('users').doc('alice').set({ name: 'Alice' });
firestore.FieldValue.serverTimestamp();
```

```js
// Now
import {
  getFirestore,
  collection,
  doc,
  setDoc,
  serverTimestamp,
} from '@react-native-firebase/firestore';

const db = getFirestore();
await setDoc(doc(collection(db, 'users'), 'alice'), {
  name: 'Alice',
  createdAt: serverTimestamp(),
});
```

See [Firestore usage](/firestore/usage) for additional examples.

# Automated migration checklist

Use this section when running scripted or agent-assisted upgrades to v26 for modular-only packages.

## 1. Upgrade dependencies

```bash
# Bump all @react-native-firebase/* packages to v26 together (monorepo versions are aligned)
yarn add @react-native-firebase/app@^26.0.0 …
cd ios && pod install
```

## 2. Fix imports by package

For each modular-only package you use:

| Package            | Search for                                                                                                                           | Replace with                                                                                                                                                   |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `app`              | default `firebase` import, `firebase.app()`, `firebase.apps`, `firebase.initializeApp()`, `firebase.utils()`, `firebase.SDK_VERSION` | `getApp()`, `getApps()`, `initializeApp()`, `getUtils()`, `FilePath`, `SDK_VERSION`, `setLogLevel`, etc.                                                       |
| `ml`               | `firebase.ml()`, default `ml()`, `FirebaseMLTypes`                                                                                   | `getML()`, root `FirebaseML` type                                                                                                                              |
| `in-app-messaging` | `firebase.inAppMessaging()`, default `inAppMessaging()`, `FirebaseInAppMessagingTypes`                                               | `getInAppMessaging()`, root modular helpers (`setMessagesDisplaySuppressed`, etc.)                                                                             |
| `installations`    | `firebase.installations()`, default `installations()`, `.getId()`, `.getToken()`, `.delete()`, `FirebaseInstallationsTypes`          | `getInstallations()`, `getId()`, `getToken()`, `deleteInstallations()`                                                                                         |
| `messaging`        | `firebase.messaging()`, default `messaging()`, `FirebaseMessagingTypes`, instance methods                                            | `getMessaging()`, root helpers (`getToken`, `onMessage`, etc.), `SDK_VERSION`, `experimentalSetDeliveryMetricsExportedToBigQueryEnabled`                       |
| `app-distribution` | `firebase.appDistribution()`, default `appDistribution()`, `FirebaseAppDistributionTypes`, instance methods                          | `getAppDistribution()`, `isTesterSignedIn`, `signInTester`, `checkForUpdate`, `signOutTester`, `SDK_VERSION`                                                   |
| `functions`        | `firebase.functions()`, default `functions()`, `FirebaseFunctionsTypes`, instance methods                                            | `getFunctions()`, `httpsCallable`, `httpsCallableFromUrl`, `connectFunctionsEmulator`, `HttpsErrorCode`                                                        |
| `perf`             | `firebase.perf()`, default `perf()`, `FirebasePerformanceTypes`, instance methods                                                    | `getPerformance()`, `trace`, `httpMetric`, `newScreenTrace`, `startScreenTrace`, `initializePerformance`                                                       |
| `app-check`        | `firebase.appCheck()`, default `appCheck()`, `FirebaseAppCheckTypes`, instance methods                                               | `getAppCheck()`, `initializeAppCheck`, `getToken`, `getLimitedUseToken`, `setTokenAutoRefreshEnabled`, `onTokenChanged`, `CustomProvider`                      |
| `analytics`        | `firebase.analytics()`, default `analytics()`, `FirebaseAnalyticsTypes`, instance methods                                            | `getAnalytics()`, `logEvent`, `setUserId`, `setConsent`, `getAppInstanceId`, `getSessionId`, etc.                                                              |
| `remote-config`    | `firebase.remoteConfig()`, default `remoteConfig()`, `FirebaseRemoteConfigTypes`, instance methods                                   | `getRemoteConfig()`, `activate`, `fetchConfig`, `fetchAndActivate`, `getValue`, `getAll`, `onConfigUpdate`, `LastFetchStatus`, `ValueSource`                   |
| `crashlytics`      | `firebase.crashlytics()`, default `crashlytics()`, `FirebaseCrashlyticsTypes`, instance methods                                      | `getCrashlytics()`, `log`, `recordError`, `setUserId`, `setAttribute`, `setAttributes`, `setCrashlyticsCollectionEnabled`, etc.                                |
| `database`         | `firebase.database()`, default `database()`, `FirebaseDatabaseTypes`, instance methods                                               | `getDatabase()`, `ref`, `refFromURL`, `onValue`, `runTransaction`, `connectDatabaseEmulator`, etc.                                                             |
| `storage`          | `firebase.storage()`, default `storage()`, `FirebaseStorageTypes`, instance/reference methods                                        | `getStorage()`, `ref`, `uploadBytesResumable`, `getDownloadURL`, `connectStorageEmulator`, `StringFormat`, `TaskEvent`, `TaskState`                            |
| `firestore`        | `firebase.firestore()`, default `firestore()`, `FirebaseFirestoreTypes`, instance methods                                            | `getFirestore()`, `doc`, `collection`, `setDoc`, `writeBatch`, `runTransaction`, `connectFirestoreEmulator`, `FieldValue`, `Timestamp`, `Bytes`, `SDK_VERSION` |

Also remove `await`, `.then()`, and `.catch()` from `UploadTask.pause()`, `UploadTask.resume()`, and `UploadTask.cancel()`. For Firestore transactions, import `TransactionOptions` when you pass `{ maxAttempts }` (omit `options` for the default **5** retries; do not pass `0` or negative values). Import `aggregateFieldEqual()` / `aggregateQuerySnapshotEqual()` for aggregate equality checks.

## 3. Validate

```bash
yarn compile
yarn tests:jest packages/<pkg>/__tests__
```
```

### Migrating to v27

Source: https://rnfirebase.io/migrating-to-v27

```mdx

Version 27 bundles several **breaking behavior corrections** and dependency bumps that landed after v26:

- **Firebase JS SDK 12.18.0** — on web/macOS (`Platform.other`), `FunctionsError.message` appends an HTTP status suffix and `customData.url` is populated ([#9218](https://github.com/invertase/react-native-firebase/pull/9218)).
- **Cloud Messaging** — four documented runtime contracts are enforced after previous accidental shapes ([#9246](https://github.com/invertase/react-native-firebase/pull/9246), [#9247](https://github.com/invertase/react-native-firebase/pull/9247), [#9252](https://github.com/invertase/react-native-firebase/pull/9252), [#9254](https://github.com/invertase/react-native-firebase/pull/9254)).
- **Imagen API removed** from `@react-native-firebase/ai` after Google shut down the Imagen models as a service on **2026-08-17**. Generate images with a Gemini image model through `getGenerativeModel()` instead.

If you are upgrading from v25 or earlier, complete [Migrating to v26](/migrating-to-v26) first — the namespaced API removal and New Architecture requirement still apply.

## Table of contents

- [Agent-assisted migration](/migrating-to-v27#agent-assisted-migration)
- [Firebase JS SDK 12.18.0](/migrating-to-v27#firebase-js-sdk-12180)
- [Cloud Messaging](/migrating-to-v27#cloud-messaging)
  - [String APNs sound → `notification.ios.sound`](/migrating-to-v27#string-apns-sound--notificationiossound)
  - [iOS `sentTime` is epoch milliseconds](/migrating-to-v27#ios-senttime-is-epoch-milliseconds)
  - [iOS badge is a string](/migrating-to-v27#ios-badge-is-a-string)
  - [`onMessageSent` receives a string message ID](/migrating-to-v27#onmessagesent-receives-a-string-message-id)
- [Removed Imagen API](/migrating-to-v27#removed-imagen-api)
  - [Example](/migrating-to-v27#example)
- [Automated migration checklist](/migrating-to-v27#automated-migration-checklist)

## Agent-assisted migration

This guide is written to be **complete enough for an automated first pass**. We recommend feeding this document to your coding agent, then asking it to analyze your codebase against each relevant section below and produce a concrete change list (or PR) for the packages you use. The [Automated migration checklist](/migrating-to-v27#automated-migration-checklist) at the end is structured for that workflow.

# Firebase JS SDK 12.18.0

**PR:** [#9218](https://github.com/invertase/react-native-firebase/pull/9218)

React Native Firebase pins **firebase-js-sdk 12.18.0**. On **web and macOS** (`Platform.other`), Cloud Functions uses the JS SDK callable path. That path now enriches `FunctionsError` (aliased as `HttpsError` in React Native Firebase):

| Change                          | Before                      | After (v27 / JS SDK 12.18.0)                                                                   | Who              |
| ------------------------------- | --------------------------- | ---------------------------------------------------------------------------------------------- | ---------------- |
| `FunctionsError.message`        | Exact server message string | Message plus a trailing ` [<httpStatus>]` suffix (for example `Invalid test requested. [400]`) | Web / macOS only |
| `FunctionsError.customData.url` | Not present                 | Request URL string on `customData`                                                             | Web / macOS only |

**Native iOS and Android** continue to surface callable failures through the native bridge as `HttpsError` **without** the HTTP status suffix. Do not assume the suffix or `customData.url` on those platforms.

### Migration actions

1. Search for exact-equality checks on `error.message` / `e.message` for callable failures.
2. On web/macOS, prefer `message.startsWith(expected)` or strip a trailing ` [digits]` before comparing.
3. If you need the HTTP status or request URL, read them from the suffix / `customData.url` on web/macOS only; keep a native fallback path for iOS/Android.

```js
// Previously (exact match — breaks on web/macOS after 12.18.0)
if (e.message === 'Invalid test requested.') {
  /* … */
}

// Now (tolerant of the optional " [status]" suffix)
const base = e.message.replace(/ \[\d+\]$/, '');
if (base === 'Invalid test requested.') {
  /* … */
}
// Optional on Platform.other:
// e.customData?.url
```

Upstream note: [firebase@12.18.0](https://github.com/firebase/firebase-js-sdk/releases/tag/firebase%4012.18.0) — “Add HTTP status code to the `message` field and add `url` to `customData`.”

# Cloud Messaging

These changes **correct runtime behavior** to match the public `RemoteMessage` / listener contracts. There is no new public TypeScript surface — consumers that worked around the old accidental shapes must update.

## String APNs sound → `notification.ios.sound`

**PR:** [#9246](https://github.com/invertase/react-native-firebase/pull/9246)

String-valued APNs sounds are serialized at the documented `message.notification.ios.sound` path. Previously, only string sounds were placed at the undocumented `message.notification.sound` path (critical-sound dictionaries already used the correct iOS nesting).

| Before (accidental)                   | After (documented)                        |
| ------------------------------------- | ----------------------------------------- |
| `message.notification.sound` (string) | `message.notification.ios.sound` (string) |

```js
// Previously
const sound = message.notification?.sound;

// Now
const sound = message.notification?.ios?.sound;
```

## iOS `sentTime` is epoch milliseconds

**PR:** [#9247](https://github.com/invertase/react-native-firebase/pull/9247)

On iOS, `RemoteMessage.sentTime` is a **number** of epoch **milliseconds**, matching Android and the public type. FCM supplies seconds (often as a string); native serialization converts those values to milliseconds.

| Before (iOS accidental)                               | After                                                       |
| ----------------------------------------------------- | ----------------------------------------------------------- |
| string epoch **seconds** (for example `"1522880044"`) | number epoch **milliseconds** (for example `1522880044000`) |

Android behavior is unchanged. Remove `parseInt` / string comparisons that assumed seconds.

```js
// Previously (iOS)
const sentMs = Number(message.sentTime) * 1000;

// Now (iOS and Android)
const sentMs = message.sentTime; // number, epoch ms
```

## iOS badge is a string

**PR:** [#9252](https://github.com/invertase/react-native-firebase/pull/9252)

iOS `aps.badge` is serialized as a **string**, matching `RemoteMessage.notification.ios.badge?: string`.

| Before (iOS accidental)  | After                      |
| ------------------------ | -------------------------- |
| number (for example `7`) | string (for example `"7"`) |

```js
// Previously
const badge = message.notification?.ios?.badge; // number on iOS

// Now
const badge = Number(message.notification?.ios?.badge ?? 0);
```

## `onMessageSent` receives a string message ID

**PR:** [#9254](https://github.com/invertase/react-native-firebase/pull/9254)

`onMessageSent` listeners receive the documented **message-ID string**. Android previously forwarded the transport wrapper `{ messageId }` to application code.

| Before (accidental)     | After (documented) |
| ----------------------- | ------------------ |
| `{ messageId: string }` | `string`           |

```js
// Previously
onMessageSent(messaging, ({ messageId }) => {
  console.log(messageId);
});

// Now
onMessageSent(messaging, messageId => {
  console.log(messageId);
});
```

# Removed Imagen API

Nothing else in `@react-native-firebase/ai` changed. `getGenerativeModel()`, `getTemplateGenerativeModel()`, `TemplateGenerativeModel`, and Gemini image generation are unaffected.

## Removed exports

{/* prettier-ignore */}
| Removed | Replacement |
| --- | --- |
| `getImagenModel` | `getGenerativeModel()` with a Gemini image model |
| `getTemplateImagenModel` | `getTemplateGenerativeModel()` with a template that uses an image model |
| `ImagenModel` | `GenerativeModel` |
| `TemplateImagenModel` | `TemplateGenerativeModel` |
| `ImagenModelParams` | `ModelParams` |
| `ImagenGenerationConfig` | `GenerationConfig` with `responseModalities` and `imageConfig` |
| `ImagenImageFormat` | No replacement — Gemini image models always return `PNG`; read `inlineData.mimeType` to confirm |
| `ImagenGenerationResponse` | `GenerateContentResult` |
| `ImagenInlineImage` | `InlineDataPart` on the response candidate |
| `ImagenGCSImage` | No replacement — Gemini image models return inline data |
| `ImagenAspectRatio` | `ImageConfigAspectRatio` |
| `ImagenSafetySettings` | `SafetySetting[]` on `getGenerativeModel()` |
| `ImagenSafetyFilterLevel` | `HarmBlockThreshold` |
| `ImagenPersonFilterLevel` | No replacement — Gemini image models allow generating people by default |

## Example

```js
// Previously (removed)
import { getApp } from '@react-native-firebase/app';
import { getAI, getImagenModel, ImagenAspectRatio } from '@react-native-firebase/ai';

const ai = getAI(getApp());
const imagenModel = getImagenModel(ai, {
  model: 'imagen-3.0-generate-002',
  generationConfig: {
    aspectRatio: ImagenAspectRatio.LANDSCAPE_16x9,
  },
});

const result = await imagenModel.generateImages('Draw a red bicycle on a beach at sunset');
console.log(result.images[0].mimeType);
```

```js
// Now
import { getApp } from '@react-native-firebase/app';
import {
  getAI,
  getGenerativeModel,
  ImageConfigAspectRatio,
  ImageConfigImageSize,
  ResponseModality,
} from '@react-native-firebase/ai';

const ai = getAI(getApp());
const model = getGenerativeModel(ai, {
  model: 'gemini-3.1-flash-image',
  generationConfig: {
    responseModalities: [ResponseModality.IMAGE],
    imageConfig: {
      aspectRatio: ImageConfigAspectRatio.LANDSCAPE_16x9,
      imageSize: ImageConfigImageSize.SIZE_1K,
    },
  },
});

const result = await model.generateContent('Draw a red bicycle on a beach at sunset');
const imagePart = result.response.inlineDataParts()?.[0];
console.log(imagePart?.inlineData.mimeType);
```

See [AI Logic usage](/ai/usage) for more image generation examples, and Google's [Imagen to Gemini migration guide](https://firebase.google.com/docs/ai-logic/imagen-models-migration) for model and prompt guidance.

# Automated migration checklist

Use this section when running scripted or agent-assisted upgrades from v26 → v27.

## 1. Upgrade dependencies

```bash
# Bump all @react-native-firebase/* packages to v27 together (monorepo versions are aligned)
yarn add @react-native-firebase/app@^27.0.0 …
cd ios && pod install
```

Confirm the lockfiles resolve **firebase** / firebase-js-sdk **12.18.0+**.

## 2. Agent-oriented search and replace

For each area you use, search the listed symbols and apply the replacements:

| Area                      | Search for                                                                             | Replace with / action                                                                         |
| ------------------------- | -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| Functions (web/macOS)     | `e.message ===`, `error.message ===` on callable failures                              | Tolerate trailing ` [<digits>]`; optionally read `customData.url` on `Platform.other` only    |
| Messaging sound           | `notification.sound` (string APNs sound)                                               | `notification.ios.sound`                                                                      |
| Messaging `sentTime`      | string/`parseInt`/`* 1000` on `sentTime` (iOS)                                         | Use numeric epoch-ms directly                                                                 |
| Messaging badge           | numeric use of `notification.ios.badge`                                                | Parse string (`Number(...)`) when a number is required                                        |
| Messaging `onMessageSent` | `({ messageId })` / `.messageId` in the listener                                       | Use the string argument directly                                                              |
| AI Imagen                 | `getImagenModel`, `getTemplateImagenModel`, `ImagenModel`, `Imagen*`, `generateImages` | `getGenerativeModel` / `getTemplateGenerativeModel` with `responseModalities` + `imageConfig` |

## 3. Validate

```bash
yarn compile
# TypeScript consumers: fix compile errors from removed Imagen exports and messaging types
# Manually exercise: callable error message handling (web/macOS), FCM notification fields (iOS), onMessageSent, Gemini image generation
```

## Related PRs (v27 breaking changes)

| Area                                       | PR                                                                    |
| ------------------------------------------ | --------------------------------------------------------------------- |
| Firebase JS SDK 12.18.0 / `FunctionsError` | [#9218](https://github.com/invertase/react-native-firebase/pull/9218) |
| Messaging — APNs string sound path         | [#9246](https://github.com/invertase/react-native-firebase/pull/9246) |
| Messaging — iOS `sentTime` epoch-ms        | [#9247](https://github.com/invertase/react-native-firebase/pull/9247) |
| Messaging — iOS badge string               | [#9252](https://github.com/invertase/react-native-firebase/pull/9252) |
| Messaging — `onMessageSent` string ID      | [#9254](https://github.com/invertase/react-native-firebase/pull/9254) |
| AI — Imagen API removal                    | [#9275](https://github.com/invertase/react-native-firebase/pull/9275) |
```

### Platforms

Source: https://rnfirebase.io/platforms

```mdx

## Status

By default React Native Firebase supports multiple platforms using the native Firebase SDK for the specific platform:

| Platform | Minimum Version            |
| -------- | -------------------------- |
| Android  | 6.0 (API 23 / Marshmallow) |
| iOS      | 15.0                       |

**Important** - To compile your application for Android, you will need a minimum JDK (Java Development Kit) version of `>= 17`.

However, for platforms that we don't provide a Native Module for, React Native Firebase instead implements a fallback Firebase JS SDK implementation to support
these 'Other' platforms, e.g.;

- Web
- macOS
- Windows
- ...and any other RN based environment.

Below is a table outlining which Firebase modules are supported on each platform in React Native Firebase:

| Firebase Service          | Android | iOS | Other |
| ------------------------- | :-----: | :-: | :---: |
| ai                        |   ✅    | ✅  |  ✅   |
| analytics                 |   ✅    | ✅  |  ✅   |
| app                       |   ✅    | ✅  |  ✅   |
| app-check                 |   ⚠️    | ⚠️  |  ⚠️   |
| app-distribution          |   ✅    | ✅  |  ❌   |
| auth                      |   ✅    | ✅  |  ⚠️   |
| crashlytics               |   ✅    | ✅  |  ❌   |
| database                  |   ✅    | ✅  |  ✅   |
| firestore                 |   ✅    | ✅  |  ⚠️   |
| functions                 |   ✅    | ✅  |  ✅   |
| in-app-messaging          |   ✅    | ✅  |  ❌   |
| installations             |   ✅    | ✅  |  ❌   |
| messaging                 |   ✅    | ✅  |  ❌   |
| ml                        |   ✅    | ✅  |  ❌   |
| perf                      |   ✅    | ✅  |  ❌   |
| phone-number-verification |   ✅    | ❌  |  ❌   |
| remote-config             |   ✅    | ✅  |  ✅   |
| storage                   |   ✅    | ✅  |  ⚠️   |

- ✅ (supported)
- ⚠️ (partial support) - see notes below
- ❌ (not supported)

## Other Platforms

Whenever the React Native Firebase SDK is running on platforms other than Android
or iOS, the internal implementation uses a fallback platform which is implemented
in JavaScript, using the [Firebase JavaScript Modular SDK](https://firebase.google.com/docs/reference/js).

No implementation changes are required to use the React Native Firebase SDK on
other platforms (with the exception of Async Storage detailed below), as the
JavaScript implementation is automatically used when a native platform is not
available. This allows you to use the same API across all platforms, regardless
of the underlying implementation.

There are however some minor limitations or differences in behavior compared
to the native platforms. Where a particular method is not supported, an error
will be thrown with a code of `unsupported` to indicate the method is not
available on the current platform.

Further details of Firebase service specific limitations are summarized below.

### Async Storage

Some services (currently Auth and Analytics) for our 'Other' platforms
implementation require a Async Storage implementation to be provided to
enable persistence, React Native Firebase provides an API to set this implementation:

```js
import { initializeApp, setReactNativeAsyncStorage } from '@react-native-firebase/app';
import AsyncStorage from '@react-native-async-storage/async-storage';

setReactNativeAsyncStorage(AsyncStorage);

await initializeApp({ ... });
```

> Note: we use `@react-native-async-storage/async-storage` as an example, you should use the Async Storage implementation that is appropriate for your platform that you are targeting.

If you do not provide an Async Storage implementation, we use an in memory implementation
which will result in resetting the data every time your app is restarted, in the case of
Firebase Auth this means your users have to sign in again and for Firebase Analytics the app will generate a new instance id and look like a new installation every time.

### Analytics

The other platform implementation of Analytics does not capture automatic metrics like screen view, you must call `logEvent` and other logging based methods to send your events to Firebase.

- [Screen Tracking Guide](/analytics/screen-tracking)

### App Check

App Check for other platforms only supports the `CustomProvider` provider. Here's how to setup your own custom provider:

- [Implement server support to get tokens](https://firebase.google.com/docs/app-check/custom-provider)
- Create a custom provider in your app:

```js
import { getApp } from '@react-native-firebase/app';
import { CustomProvider, initializeAppCheck } from '@react-native-firebase/app-check';

const myCustomProvider = new CustomProvider({
  async getToken: () => {
    const tokenFromServer = 'some-token-from-server';
    const expirationFromServer = 1000 * 60 * 60;
    const appCheckToken = {
      token: tokenFromServer,
      expireTimeMillis: expirationFromServer * 1000
    };
    return appCheckToken;
  }
});

await initializeAppCheck(getApp(), {
  provider: myCustomProvider
});
```

### Authentication

Multi-factor authentication is not supported on other platforms.

Phone authentication methods are unsupported, specifically:

- `signInWithProvider`
- `signInWithPhoneNumber`
- `verifyPhoneNumberForMultiFactor`
- `confirmationResultConfirm`
- `verifyPhoneNumber`
- `reauthenticateWithProvider`

### Database

Offline persistence is not supported on other platforms.

Unsupported methods:

- `keepSynced` - for offline persistence

### Firestore

For performance reasons and to reduce the size of the JavaScript bundle, the Other platform implementation in
React Native Firebase uses the JavaScript [lite](https://firebase.google.com/docs/reference/js/firestore_lite) SDK,
which does not support methods related to offline & persistence.

Specifically, the following methods are not supported:

- `loadBundle`
- `clearPersistence` - for offline persistence
- `disableNetwork` - for offline persistence
- `enableNetwork` - for offline persistence
- `namedQuery`
- `onSnapshot` (for both `CollectionReference` & `DocumentReference`)
- `GetOptions.source`

### Storage

No-op methods:

- `setMaxDownloadRetryTime` (does not throw, but has no effect)

Unsupported methods:

- `writeToFile`
- `putFile`

---

## Known Issues

Known issues will be documented here and updated regularly.

- There are currently no known issues.
```

### Release notes

Source: https://rnfirebase.io/releases

```mdx

Starting from version `10.0.0`, React Native Firebase packages share a single common version, with aggregated release notes available:

![hide:badge](https://img.shields.io/npm/v/@react-native-firebase/app.svg?style=for-the-badge&logo=npm) [View Release Notes &raquo;](https://github.com/invertase/react-native-firebase/tree/main/CHANGELOG.md)

---

From version `v6.5.0` until `10.0.0`; all React Native Firebase packages were independently versioned with individually generated release notes:

| Package                |                                                                                                                      |                                                                                                                                   |
| ---------------------- | :------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------: |
| Analytics              |    ![hide:badge](https://img.shields.io/npm/v/@react-native-firebase/analytics.svg?style=for-the-badge&logo=npm)     |    [View Release Notes &raquo;](https://github.com/invertase/react-native-firebase/tree/main/packages/analytics/CHANGELOG.md)     |
| App                    |       ![hide:badge](https://img.shields.io/npm/v/@react-native-firebase/app.svg?style=for-the-badge&logo=npm)        |       [View Release Notes &raquo;](https://github.com/invertase/react-native-firebase/tree/main/packages/app/CHANGELOG.md)        |
| App Check              |    ![hide:badge](https://img.shields.io/npm/v/@react-native-firebase/app-check.svg?style=for-the-badge&logo=npm)     |    [View Release Notes &raquo;](https://github.com/invertase/react-native-firebase/tree/main/packages/app-check/CHANGELOG.md)     |
| App Distribution       | ![hide:badge](https://img.shields.io/npm/v/@react-native-firebase/app-distribution.svg?style=for-the-badge&logo=npm) | [View Release Notes &raquo;](https://github.com/invertase/react-native-firebase/tree/main/packages/app-distribution/CHANGELOG.md) |
| Authentication         |       ![hide:badge](https://img.shields.io/npm/v/@react-native-firebase/auth.svg?style=for-the-badge&logo=npm)       |       [View Release Notes &raquo;](https://github.com/invertase/react-native-firebase/tree/main/packages/auth/CHANGELOG.md)       |
| Cloud Firestore        |    ![hide:badge](https://img.shields.io/npm/v/@react-native-firebase/firestore.svg?style=for-the-badge&logo=npm)     |    [View Release Notes &raquo;](https://github.com/invertase/react-native-firebase/tree/main/packages/firestore/CHANGELOG.md)     |
| Cloud Functions        |    ![hide:badge](https://img.shields.io/npm/v/@react-native-firebase/functions.svg?style=for-the-badge&logo=npm)     |    [View Release Notes &raquo;](https://github.com/invertase/react-native-firebase/tree/main/packages/functions/CHANGELOG.md)     |
| Cloud Messaging        |    ![hide:badge](https://img.shields.io/npm/v/@react-native-firebase/messaging.svg?style=for-the-badge&logo=npm)     |    [View Release Notes &raquo;](https://github.com/invertase/react-native-firebase/tree/main/packages/messaging/CHANGELOG.md)     |
| Cloud Storage          |     ![hide:badge](https://img.shields.io/npm/v/@react-native-firebase/storage.svg?style=for-the-badge&logo=npm)      |     [View Release Notes &raquo;](https://github.com/invertase/react-native-firebase/tree/main/packages/storage/CHANGELOG.md)      |
| Crashlytics            |   ![hide:badge](https://img.shields.io/npm/v/@react-native-firebase/crashlytics.svg?style=for-the-badge&logo=npm)    |   [View Release Notes &raquo;](https://github.com/invertase/react-native-firebase/tree/main/packages/crashlytics/CHANGELOG.md)    |
| In-app Messaging       | ![hide:badge](https://img.shields.io/npm/v/@react-native-firebase/in-app-messaging.svg?style=for-the-badge&logo=npm) | [View Release Notes &raquo;](https://github.com/invertase/react-native-firebase/tree/main/packages/in-app-messaging/CHANGELOG.md) |
| Installations          |  ![hide:badge](https://img.shields.io/npm/v/@react-native-firebase/installations.svg?style=for-the-badge&logo=npm)   |  [View Release Notes &raquo;](https://github.com/invertase/react-native-firebase/tree/main/packages/installations/CHANGELOG.md)   |
| ML                     |        ![hide:badge](https://img.shields.io/npm/v/@react-native-firebase/ml.svg?style=for-the-badge&logo=npm)        |        [View Release Notes &raquo;](https://github.com/invertase/react-native-firebase/tree/main/packages/ml/CHANGELOG.md)        |
| Performance Monitoring |       ![hide:badge](https://img.shields.io/npm/v/@react-native-firebase/perf.svg?style=for-the-badge&logo=npm)       |       [View Release Notes &raquo;](https://github.com/invertase/react-native-firebase/tree/main/packages/perf/CHANGELOG.md)       |
| Realtime Database      |     ![hide:badge](https://img.shields.io/npm/v/@react-native-firebase/database.svg?style=for-the-badge&logo=npm)     |     [View Release Notes &raquo;](https://github.com/invertase/react-native-firebase/tree/main/packages/database/CHANGELOG.md)     |
| Remote Config          |  ![hide:badge](https://img.shields.io/npm/v/@react-native-firebase/remote-config.svg?style=for-the-badge&logo=npm)   |  [View Release Notes &raquo;](https://github.com/invertase/react-native-firebase/tree/main/packages/remote-config/CHANGELOG.md)   |

---

To view release notes for versions prior to `v6.5.0` see the table below.

| Version |                                                |
| ------- | :--------------------------------------------: |
| v6.4.0  | [View Release Notes &raquo;](/releases/v6.4.0) |
| v6.3.0  | [View Release Notes &raquo;](/releases/v6.3.0) |
| v6.2.0  | [View Release Notes &raquo;](/releases/v6.2.0) |
| v6.1.0  | [View Release Notes &raquo;](/releases/v6.1.0) |
| v6.0.3  | [View Release Notes &raquo;](/releases/v6.0.3) |
| v6.0.2  | [View Release Notes &raquo;](/releases/v6.0.2) |
| v6.0.1  | [View Release Notes &raquo;](/releases/v6.0.1) |
| v6.0.0  | [View Release Notes &raquo;](/releases/v6.0.0) |
```

### TypeScript

Source: https://rnfirebase.io/typescript

```mdx

React Native Firebase ships TypeScript declarations for every module. From v25 onward, modular types and helpers are exported from each package root (for example `@react-native-firebase/auth`) alongside the namespaced default export.

If you are setting up TypeScript in a new React Native app, see the official [TypeScript documentation](https://reactnative.dev/docs/typescript).

## Modular API

Import modular helpers and types from the package root:

```tsx
import { useEffect, useState } from 'react';
import { getAuth, onAuthStateChanged, type User } from '@react-native-firebase/auth';

function App() {
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    const authInstance = getAuth();
    return onAuthStateChanged(authInstance, setUser);
  }, []);

  if (!user) {
    return null;
  }

  return user.email;
}
```

See [Migrating to v25](/migrating-to-v25) for Auth breaking changes (`FirebaseAuthTypes` deprecation, provider helpers, async `isSignInWithEmailLink`, and other firebase-js-sdk alignment updates).

## Where definitions live

Published types are built to `packages/<module>/dist/typescript/` (for example `packages/auth/dist/typescript/lib/index.d.ts`). Source lives under `packages/<module>/lib/` as TypeScript (`.ts`) since the v25 package migrations.

Modular Auth source: [`packages/auth/lib/modular.ts`](https://github.com/invertase/react-native-firebase/blob/main/packages/auth/lib/modular.ts).

Public API reference (generated from source JSDoc): [reference API](https://reference.rnfirebase.io/modules.html).

## Definitions per module

Each package exports its own types from the package root. Examples:

| Module    | Modular types (import from package root)                                                                  |
| --------- | --------------------------------------------------------------------------------------------------------- |
| Auth      | `User`, `Auth`, `UserCredential`, `OAuthProvider`, …                                                      |
| Firestore | `FirebaseFirestoreTypes` namespace (namespaced) + modular helpers from `@react-native-firebase/firestore` |
| App Check | `AppCheck`, `AppCheckTokenResult`, …                                                                      |

For Auth v25 alignment details and intentional firebase-js-sdk differences, see the [Auth compare:types triage](https://github.com/invertase/react-native-firebase/blob/main/okf-bundle/packages/auth/compare-types-triage.md) knowledge document and [`yarn compare:types auth`](https://github.com/invertase/react-native-firebase/blob/main/.github/scripts/compare-types/configs/auth.ts) registry.
```

### AI Logic

Source: https://rnfirebase.io/ai/usage

```mdx

# Installation

This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app" module, view the
[Getting Started](/) documentation.

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the ai module
yarn add @react-native-firebase/ai
```

# Platform support

|                      |                                                                        |
| -------------------- | ---------------------------------------------------------------------- |
| **Platforms**        | Android, iOS, Web (firebase-js-sdk interop)                            |
| **New Architecture** | **Not required** — pure JavaScript package with no native TurboModule. |

# What does it do

Firebase AI Logic gives you access to the latest generative AI models from Google.

If you need to call the Gemini API directly from your mobile or web app — rather than server-side — you can use the Firebase AI Logic client SDKs. These client SDKs are built specifically for use with mobile and web apps, offering security options against unauthorized clients as well as integrations with other Firebase services.

# Usage

## Generate text from text-only input

You can call the Gemini API with input that includes only text. For these calls, you need to use a model that supports text-only prompts (like Gemini 3.1 Flash-Lite).

Use `generateContent()` which waits for the entire response before returning.

```js
import React from 'react';
import { AppRegistry, Button, Text, View } from 'react-native';
import { getApp } from '@react-native-firebase/app';
import { getAI, getGenerativeModel } from '@react-native-firebase/ai';

function App() {
  return (
    <View>
      <Button
        title="generate content"
        onPress={async () => {
          const app = getApp();
          const ai = getAI(app);
          const model = getGenerativeModel(ai, { model: 'gemini-3.1-flash-lite' });

          const result = await model.generateContent('What is 2 + 2?');

          console.log(result.response.text());
        }}
      />
    </View>
  );
}
```

Use `generateContentStream()` if you wish to stream the response.

```js
import React from 'react';
import { AppRegistry, Button, Text, View } from 'react-native';
import { getApp } from '@react-native-firebase/app';
import { getAI, getGenerativeModel } from '@react-native-firebase/ai';

function App() {
  return (
    <View>
      <Button
        title="generate content stream"
        onPress={async () => {
          const app = getApp();
          const ai = getAI(app);
          const model = getGenerativeModel(ai, { model: 'gemini-3.1-flash-lite' });

          const result = await model.generateContentStream('Write a short poem');

          let text = '';
          for await (const chunk of result.stream) {
            const chunkText = chunk.text();
            text += chunkText;
          }

          console.log(text);

          const response = await result.response;
          // Optional: use metadata (e.g. groundingMetadata when grounding is enabled)
          if (response.candidates?.[0]?.groundingMetadata) {
            console.log('Grounding metadata', response.candidates[0].groundingMetadata);
          }
        }}
      />
    </View>
  );
}
```

## Generate text from multi-modal input

You can pass in different input types to generate text responses. **important** - React Native does not have native support for `Blob` and `Buffer` types which might be used to facilitate different modal inputs. You may have to use third party libraries for this functionality.

```js
import React from 'react';
import { AppRegistry, Button, Text, View } from 'react-native';
import { getApp } from '@react-native-firebase/app';
import { getAI, getGenerativeModel } from '@react-native-firebase/ai';

function App() {
  return (
    <View>
      <Button
        title="generate content stream multi-modal"
        onPress={async () => {
          const app = getApp();
          const ai = getAI(app);
          const model = getGenerativeModel(ai, { model: 'gemini-3.1-flash-lite' });
          const prompt = 'What can you see?';
          const base64Emoji =
            'iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFaooFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1ZAqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4TFLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHirr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWLdwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII=';

          const response = await model.generateContentStream([
            prompt,
            { inlineData: { mimeType: 'image/png', data: base64Emoji } },
          ]);

          let text = '';
          for await (const chunk of response.stream) {
            text += chunk.text();
          }

          console.log(text);
        }}
      />
    </View>
  );
}
```

## Generate structured output (e.g. JSON)

The Firebase AI Logic SDK returns responses as unstructured text by default. However, some use cases require structured text, like JSON. For example, you might be using the response for other downstream tasks that require an established data schema.

```js
import React from 'react';
import { AppRegistry, Button, Text, View } from 'react-native';
import { getApp } from '@react-native-firebase/app';
import { getAI, getGenerativeModel } from '@react-native-firebase/ai';

function App() {
  return (
    <View>
      <Button
        title="generate structured output"
        onPress={async () => {
          const app = getApp();
          const ai = getAI(app);
          const jsonSchema = Schema.object({
            properties: {
              characters: Schema.array({
                items: Schema.object({
                  properties: {
                    name: Schema.string(),
                    accessory: Schema.string(),
                    age: Schema.number(),
                    species: Schema.string(),
                  },
                  optionalProperties: ['accessory'],
                }),
              }),
            },
          });
          const model = getGenerativeModel(ai, {
            model: 'gemini-3.1-flash-lite',
            generationConfig: {
              responseMimeType: 'application/json',
              responseSchema: jsonSchema,
            },
          });

          let prompt = "For use in a children's card game, generate 10 animal-based characters.";

          let result = await model.generateContent(prompt);
          console.log(result.response.text());
        }}
      />
    </View>
  );
}
```

## Multi-turn conversations

You can build freeform conversations across multiple turns. The Firebase AI Logic SDK simplifies the process by managing the state of the conversation, so unlike with `generateContentStream()` or `generateContent()`, you don't have to store the conversation history yourself.

```js
import React from 'react';
import { AppRegistry, Button, Text, View } from 'react-native';
import { getApp } from '@react-native-firebase/app';
import { getAI, getGenerativeModel } from '@react-native-firebase/ai';

function App() {
  return (
    <View>
      <Button
        title="start chat session"
        onPress={async () => {
          const app = getApp();
          const ai = getAI(app);
          const model = getGenerativeModel(ai, { model: 'gemini-3.1-flash-lite' });

          const chat = model.startChat({
            history: [
              {
                role: 'user',
                parts: [{ text: 'Hello, I have 2 dogs in my house.' }],
              },
              {
                role: 'model',
                parts: [{ text: 'Great to meet you. What would you like to know?' }],
              },
            ],
            generationConfig: {
              maxOutputTokens: 100,
            },
          });

          const msg = 'How many paws are in my house?';
          const result = await chat.sendMessageStream(msg);

          let text = '';
          for await (const chunk of result.stream) {
            const chunkText = chunk.text();
            text += chunkText;
          }
          console.log(text);

          // When you want to see the history of the chat
          const history = await chat.getHistory();
          console.log(history);
        }}
      />
    </View>
  );
}
```

## Function calling

Generative models are powerful at solving many types of problems. However, they are constrained by limitations like:

- They are frozen after training, leading to stale knowledge.
- They can't query or modify external data.

Function calling can help you overcome some of these limitations. Function calling is sometimes referred to as tool use because it allows a model to use external tools such as APIs and functions to generate its final response.

```js
import React from 'react';
import { AppRegistry, Button, Text, View } from 'react-native';
import { getApp } from '@react-native-firebase/app';
import { getAI, getGenerativeModel } from '@react-native-firebase/ai';

function App() {
  return (
    <View>
      <Button
        title="function calling"
        onPress={async () => {
          // This function calls a hypothetical external API that returns
          // a collection of weather information for a given location on a given date.
          // `location` is an object of the form { city: string, state: string }
          async function fetchWeather({ location, date }) {
            // For demo purposes, this hypothetical response is hardcoded here in the expected format.
            return {
              temperature: 38,
              chancePrecipitation: '56%',
              cloudConditions: 'partlyCloudy',
            };
          }
          const fetchWeatherTool = {
            functionDeclarations: [
              {
                name: 'fetchWeather',
                description: 'Get the weather conditions for a specific city on a specific date',
                parameters: Schema.object({
                  properties: {
                    location: Schema.object({
                      description:
                        'The name of the city and its state for which to get ' +
                        'the weather. Only cities in the USA are supported.',
                      properties: {
                        city: Schema.string({
                          description: 'The city of the location.',
                        }),
                        state: Schema.string({
                          description: 'The US state of the location.',
                        }),
                      },
                    }),
                    date: Schema.string({
                      description:
                        'The date for which to get the weather. Date must be in the' +
                        ' format: YYYY-MM-DD.',
                    }),
                  },
                }),
              },
            ],
          };
          const app = getApp();
          const ai = getAI(app);
          const model = getGenerativeModel(ai, {
            model: 'gemini-3.1-flash-lite',
            tools: fetchWeatherTool,
          });

          const chat = model.startChat();
          const prompt = 'What was the weather in Boston on October 17, 2024?';

          // Send the user's question (the prompt) to the model using multi-turn chat.
          let result = await chat.sendMessage(prompt);
          const functionCalls = result.response.functionCalls();
          let functionCall;
          let functionResult;
          // When the model responds with one or more function calls, invoke the function(s).
          if (functionCalls.length > 0) {
            for (const call of functionCalls) {
              if (call.name === 'fetchWeather') {
                // Forward the structured input data prepared by the model
                // to the hypothetical external API.
                functionResult = await fetchWeather(call.args);
                functionCall = call;
              }
            }
          }
          result = await chat.sendMessage([
            {
              functionResponse: {
                name: functionCall.name, // "fetchWeather"
                response: functionResult,
              },
            },
          ]);
          console.log(result.response.text());
        }}
      />
    </View>
  );
}
```

## Count tokens & billable characters

Generative AI models break down data into units called tokens for processing. Each Gemini model has a [maximum number of tokens](https://firebase.google.com/docs/ai-logic/models) that it can handle in a prompt and response.

The below shows you how to get an estimate of token count and the number of billable characters for a request.

On newer Gemini models (for example, `gemini-3.1-flash-lite`), `totalBillableCharacters` may be **undefined**. Check for its presence before logging or billing on that field.

```js
import React from 'react';
import { AppRegistry, Button, Text, View } from 'react-native';
import { getApp } from '@react-native-firebase/app';
import { getAI, getGenerativeModel } from '@react-native-firebase/ai';

function App() {
  return (
    <View>
      <Button
        title="count tokens and billable characters"
        onPress={async () => {
          const app = getApp();
          const ai = getAI(app);
          const model = getGenerativeModel(ai, { model: 'gemini-3.1-flash-lite' });
          // Count tokens & billable character for text input
          const { totalTokens, totalBillableCharacters } = await model.countTokens(
            'Write a story about a magic backpack.',
          );
          console.log(`Total tokens: ${totalTokens}`);
          if (totalBillableCharacters !== undefined) {
            console.log(`Total billable characters: ${totalBillableCharacters}`);
          }

          // Count tokens & billable character for multi-modal input
          const prompt = "What's in this picture?";
          const imageAsBase64 = '...base64 string image';
          const imagePart = { inlineData: { mimeType: 'image/jpeg', data: imageAsBase64 } };

          const { totalTokens, totalBillableCharacters } = await model.countTokens([
            prompt,
            imagePart,
          ]);
          console.log(`Total tokens: ${totalTokens}`);
          if (totalBillableCharacters !== undefined) {
            console.log(`Total billable characters: ${totalBillableCharacters}`);
          }
        }}
      />
    </View>
  );
}
```

## Generate images with Gemini models

Some Gemini models can return generated images in addition to text. Configure `generationConfig.responseModalities` and `generationConfig.imageConfig` on `getGenerativeModel()` to control output format, aspect ratio, and size.

See the [Firebase AI Logic image generation guide](https://firebase.google.com/docs/ai-logic/generate-images-gemini) for supported models and limits.

```js
import React from 'react';
import { Button, View } from 'react-native';
import { getApp } from '@react-native-firebase/app';
import {
  getAI,
  getGenerativeModel,
  ImageConfigAspectRatio,
  ImageConfigImageSize,
  ResponseModality,
} from '@react-native-firebase/ai';

function App() {
  return (
    <View>
      <Button
        title="generate image with Gemini"
        onPress={async () => {
          const app = getApp();
          const ai = getAI(app);
          const model = getGenerativeModel(ai, {
            model: 'gemini-3.1-flash-image',
            generationConfig: {
              responseModalities: [ResponseModality.IMAGE],
              imageConfig: {
                aspectRatio: ImageConfigAspectRatio.LANDSCAPE_16x9,
                imageSize: ImageConfigImageSize.SIZE_1K,
              },
            },
          });

          const result = await model.generateContent('Draw a red bicycle on a beach at sunset');
          const parts = result.response.candidates?.[0]?.content?.parts ?? [];
          const imagePart = parts.find(part => part.inlineData?.mimeType?.startsWith('image/'));
          console.log('Generated image mime type', imagePart?.inlineData?.mimeType);
        }}
      />
    </View>
  );
}
```

## Ground responses with Google Maps

You can give Gemini access to Google Maps grounding by adding a `googleMaps` tool. Pass `toolConfig.retrievalConfig` with the user's location and language so responses can incorporate location-aware information.

When using Grounding with Google Maps you must comply with the [Gemini API terms](https://ai.google.dev/gemini-api/terms#grounding-with-google-maps) for your API provider.

```js
import React from 'react';
import { Button, View } from 'react-native';
import { getApp } from '@react-native-firebase/app';
import { getAI, getGenerativeModel } from '@react-native-firebase/ai';

function App() {
  return (
    <View>
      <Button
        title="ask with Google Maps grounding"
        onPress={async () => {
          const app = getApp();
          const ai = getAI(app);
          const model = getGenerativeModel(ai, {
            model: 'gemini-3.1-flash-lite',
            tools: [{ googleMaps: {} }],
            toolConfig: {
              retrievalConfig: {
                latLng: { latitude: 37.7749, longitude: -122.4194 },
                languageCode: 'en-US',
              },
            },
          });

          const result = await model.generateContent(
            'What are some highly rated coffee shops within walking distance?',
          );
          console.log(result.response.text());
          console.log(result.response.candidates?.[0]?.groundingMetadata);
        }}
      />
    </View>
  );
}
```

## Retrieval config for tools

`toolConfig.retrievalConfig` applies to all tools on a request. Use it to pass the user's `latLng` and `languageCode` when tools need geographic or locale context (for example Google Maps grounding or retrieval-backed tools).

```js
import React from 'react';
import { Button, View } from 'react-native';
import { getApp } from '@react-native-firebase/app';
import { getAI, getGenerativeModel } from '@react-native-firebase/ai';

function App() {
  return (
    <View>
      <Button
        title="generate with retrieval config"
        onPress={async () => {
          const app = getApp();
          const ai = getAI(app);
          const model = getGenerativeModel(ai, {
            model: 'gemini-3.1-flash-lite',
            tools: [{ googleSearch: {} }],
            toolConfig: {
              retrievalConfig: {
                latLng: { latitude: 51.5074, longitude: -0.1278 },
                languageCode: 'en-GB',
              },
            },
          });

          const result = await model.generateContent(
            'What events are happening nearby this weekend?',
          );
          console.log(result.response.text());
        }}
      />
    </View>
  );
}
```

## Live sessions

The Live API enables low-latency, bidirectional interaction with Gemini over a persistent connection. Use `getLiveGenerativeModel()` and `connect()` to obtain a `LiveSession`, then send and receive messages in real time.

Browser-only helpers such as `startAudioConversation()` and `AudioConversationController` are not available in React Native. Use `sendTextRealtime()`, `sendAudioRealtime()`, and `sendVideoRealtime()` on `LiveSession` instead.

See the [Firebase Live API documentation](https://firebase.google.com/docs/ai-logic/live-api) for model availability and audio format requirements.

```js
import React from 'react';
import { Button, View } from 'react-native';
import { getApp } from '@react-native-firebase/app';
import { getAI, getLiveGenerativeModel } from '@react-native-firebase/ai';

function App() {
  return (
    <View>
      <Button
        title="start live session"
        onPress={async () => {
          const app = getApp();
          const ai = getAI(app);
          const liveModel = getLiveGenerativeModel(ai, {
            model: 'gemini-2.5-flash-native-audio-preview-12-2025',
            generationConfig: { temperature: 0.8 },
          });

          const session = await liveModel.connect();

          (async () => {
            for await (const message of session.receive()) {
              if (message.type === 'serverContent' && message.modelTurn) {
                const text = message.modelTurn.parts?.map(part => part.text).join('');
                if (text) {
                  console.log('Model:', text);
                }
              } else if (message.type === 'toolCall') {
                console.log('Tool call', message.functionCalls);
              } else if (message.type === 'goingAwayNotice') {
                console.log('Connection closing in', message.timeLeft, 'seconds');
              }
            }
          })();

          await session.sendTextRealtime('Hello! Can you hear me?');
          await session.close();
        }}
      />
    </View>
  );
}
```

## Context window compression (Live API)

Long-running Live sessions can exceed the model context window. Set `generationConfig.contextWindowCompression` on `getLiveGenerativeModel()` to enable server-side sliding-window compression when token usage crosses a threshold.

```js
import React from 'react';
import { Button, View } from 'react-native';
import { getApp } from '@react-native-firebase/app';
import { getAI, getLiveGenerativeModel } from '@react-native-firebase/ai';

function App() {
  return (
    <View>
      <Button
        title="live session with context compression"
        onPress={async () => {
          const app = getApp();
          const ai = getAI(app);
          const liveModel = getLiveGenerativeModel(ai, {
            model: 'gemini-2.5-flash-native-audio-preview-12-2025',
            generationConfig: {
              contextWindowCompression: {
                triggerTokens: 100000,
                slidingWindow: { targetTokens: 80000 },
              },
            },
          });

          const session = await liveModel.connect();
          await session.send('Start a long conversation...');
          await session.close();
        }}
      />
    </View>
  );
}
```

## Session resumption (Live API)

Live sessions can be resumed after a disconnect. Pass a `SessionResumptionConfig` to `connect()` (use `{}` to opt in to resumption updates). The server sends `sessionResumptionUpdate` messages on `LiveSession.receive()` with a handle you can pass to a later `connect()` call or to `LiveSession.resumeSession()`.

`resumeSession()` requires that the original session was opened with session resumption enabled.

```js
import React from 'react';
import { Button, View } from 'react-native';
import { getApp } from '@react-native-firebase/app';
import { getAI, getLiveGenerativeModel } from '@react-native-firebase/ai';

function App() {
  return (
    <View>
      <Button
        title="resume live session"
        onPress={async () => {
          const app = getApp();
          const ai = getAI(app);
          const liveModel = getLiveGenerativeModel(ai, {
            model: 'gemini-2.5-flash-native-audio-preview-12-2025',
          });

          let resumptionHandle;
          const session = await liveModel.connect({});

          (async () => {
            for await (const message of session.receive()) {
              if (message.type === 'sessionResumptionUpdate' && message.resumable) {
                resumptionHandle = message.newHandle;
              }
            }
          })();

          await session.send('Start a conversation we can resume later.');

          // After a disconnect or `goingAwayNotice`, reconnect with the saved handle:
          if (resumptionHandle) {
            await session.resumeSession({ handle: resumptionHandle });
            await session.send('Pick up where we left off.');
          }

          await session.close();
        }}
      />
    </View>
  );
}
```

Alternatively, call `resumeSession({ handle })` on an open session after a `goingAwayNotice` without closing first:

```js
await session.resumeSession({ handle: resumptionHandle });
await session.connectionPromise;
```

## Template models and retrieval config

`TemplateGenerativeModel` executes server-side prompt templates. You can pass an optional `templateToolConfig` with `retrievalConfig` to `generateContent()`, `generateContentStream()`, or `startChat()`.

```js
import React from 'react';
import { Button, View } from 'react-native';
import { getApp } from '@react-native-firebase/app';
import { getAI, getTemplateGenerativeModel } from '@react-native-firebase/ai';

function App() {
  return (
    <View>
      <Button
        title="run template with retrieval config"
        onPress={async () => {
          const app = getApp();
          const ai = getAI(app);
          const templateModel = getTemplateGenerativeModel(ai);

          const result = await templateModel.generateContent(
            'weather-assistant-template',
            { city: 'Boston' },
            undefined,
            {
              retrievalConfig: {
                latLng: { latitude: 42.3601, longitude: -71.0589 },
                languageCode: 'en-US',
              },
            },
          );

          console.log(result.response.text());
        }}
      />
    </View>
  );
}
```

## Getting ready for production

For mobile and web apps, you need to protect the Gemini API and your project resources (like tuned models) from abuse by unauthorized clients. You can use Firebase App Check to verify that all API calls are from your actual app. See [Firebase docs for further information](https://firebase.google.com/docs/ai-logic/app-check).

- Ensure you have setup [App Check for React Native Firebase](/app-check/usage/index)
- Pass in an instance of App Check to Firebase AI Logic which, under the hood, will call `appCheck.getToken()` and use it as part of Firebase AI Logic API requests to the server.

```js
import React from 'react';
import { AppRegistry, Button, Text, View } from 'react-native';
import { getApp } from '@react-native-firebase/app';
import { getAuth } from '@react-native-firebase/auth';
import { initializeAppCheck } from '@react-native-firebase/app-check';
import { getAI, getGenerativeModel, GoogleAIBackend } from '@react-native-firebase/ai';

function App() {
  return (
    <View>
      <Button
        title="use App Check and pass into getAI()"
        onPress={async () => {
          const app = getApp();
          const authInstance = getAuth(app);
          const appCheckInstance = await initializeAppCheck(app, {
            // Configure App Check as per docs...
          });
          const options = {
            appCheck: appCheckInstance,
            auth: authInstance,
            backend: new GoogleAIBackend(),
          };

          const ai = getAI(app, options);
          const model = getGenerativeModel(ai, { model: 'gemini-3.1-flash-lite' });

          const result = await model.generateContent('What is 2 + 2?');

          console.log('result', result.response.text());
        }}
      />
    </View>
  );
}
```
```

### Screen Tracking

Source: https://rnfirebase.io/analytics/screen-tracking

```mdx

Standard React Native applications run inside a single `Activity`/`ViewController`, meaning any screen changes won't be
tracked by the native Firebase SDKs. There are a number of ways to implement navigation within React Native apps,
therefore there is no "one fits all" solution to screen tracking.

# React Navigation

The [React Navigation](https://reactnavigation.org/) library allows for various navigation techniques such as
Stack, Tab, Native or even custom navigation. The `NavigationContainer` component which the library exposes provides
access to the current navigation state when a screen changes, allowing you to use the [`logScreenView`](https://invertase.github.io/react-native-firebase/_react-native-firebase/analytics/modular/logScreenView.html)
method the Analytics library provides:

```jsx
import { getAnalytics, logScreenView } from '@react-native-firebase/analytics';
import { NavigationContainer } from '@react-navigation/native';

const App = () => {
  const routeNameRef = React.useRef();
  const navigationRef = React.useRef();
  return (
    <NavigationContainer
      ref={navigationRef}
      onReady={() => {
        routeNameRef.current = navigationRef.current.getCurrentRoute().name;
      }}
      onStateChange={async () => {
        const previousRouteName = routeNameRef.current;
        const currentRouteName = navigationRef.current.getCurrentRoute().name;

        if (previousRouteName !== currentRouteName) {
          await logScreenView(getAnalytics(), {
            screen_name: currentRouteName,
            screen_class: currentRouteName,
          });
        }
        routeNameRef.current = currentRouteName;
      }}
    >
      ...
    </NavigationContainer>
  );
};

export default App;
```

For a full working example, view the [Screen tracking for analytics](https://reactnavigation.org/docs/screen-tracking/)
documentation on the React Navigation website.

# React Native Navigation

The [`wix/react-native-navigation`](https://github.com/wix/react-native-navigation) provides 100% native platform navigation
for React Native apps. To manually track screens, you need to setup a `componentDidAppear` event listener and manually call the
[`logScreenView`](https://invertase.github.io/react-native-firebase/_react-native-firebase/analytics/modular/logScreenView.html) method the Analytics library provides:

```js
import { getAnalytics, logScreenView } from '@react-native-firebase/analytics';
import { Navigation } from 'react-native-navigation';

Navigation.events().registerComponentDidAppearListener(async ({ componentName, componentType }) => {
  if (componentType === 'Component') {
    await logScreenView(getAnalytics(), {
      screen_name: componentName,
      screen_class: componentName,
    });
  }
});
```

To learn more, view the [events documentation](https://wix.github.io/react-native-navigation/api/events#componentdidappear)
on the React Native Navigation website.
```

### Analytics

Source: https://rnfirebase.io/analytics/usage

```mdx

# Installation

This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app" module, view the
[Getting Started](/) documentation.

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the analytics module
yarn add @react-native-firebase/analytics

# If you're developing your app using iOS, run this command
cd ios/ && pod install
```

If you're using an older version of React Native without autolinking support, or wish to integrate into an existing project,
you can follow the manual installation steps for [iOS](/analytics/usage/installation/ios) and [Android](/analytics/usage/installation/android).

# Platform support and New Architecture

|                      |                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| **Platforms**        | Android, iOS (native Firebase SDK)                                                             |
| **New Architecture** | **Required** from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement). |

**Platform notes:** On-device conversion measurement helpers (`initiateOnDeviceConversionMeasurement*`) are **iOS only**; on Android and web the JS layer resolves without calling native (no-op). `logTransaction` (verified iOS in-app purchase events) is **iOS only** and **rejects** on other platforms. Analytics supports only the default Firebase app.

# What does it do

Analytics collects usage and behavior data for your app. Its two primary concerns are:

- **Events**: What is happening in your app, such as user actions, system events, or errors.
- **User properties**: Attributes you define to describe segments of your user base, such as language preference or geographic location.

<YouTube id="8iZpH7O6zXo" />

Analytics automatically logs some [events](https://support.google.com/analytics/answer/9234069) and [user properties](https://support.google.com/analytics/answer/9268042); you don't need to add any code to enable them. However, Analytics also allows you to log [custom](/analytics/usage#custom-events) or [predefined](/analytics/usage#predefined-events) events within your app. How you can do this will be explained below.

# Usage

Analytics offers a wealth of [Predefined Events](/analytics/usage#predefined-events) to track user behavior. Analytics also offers folks the ability to log [Custom Events](/analytics/usage#custom-events) . If you're already familiar with Google Analytics, this method is equivalent to using the event command in [gtag.js](https://developers.google.com/gtagjs/).

## Event Parameters

Please pay very special attention to what parameters you send in for _any_ events - custom, predefined or otherwise.

> **WARNING**
> Parameters are _not_ validated and incorrect parameters will _silently_ be accepted but then _fail to log an event_ in the Analytics console.

It is the developer's responsibility to verify that their parameters are correct and are being logged correctly.

Different event types require different parameters (some require no parameters, some require an array of strings, most require just a string, etc). The developer must examine the reference for each type of event and send the correct parameters. You may watch device logs and the Analytics console to make sure the events are correctly sent to Google Analytics.

## Custom Events

Below is an example showing how a custom event can be logged. Please be aware that primitive data types or arrays of primitive data types are logged in your Firebase Analytics console.

```jsx
import react, { useEffect } from 'react';
import { View, Button } from 'react-native';
import { getAnalytics, logEvent } from '@react-native-firebase/analytics';

function App() {
  return (
    <View>
      <Button
        title="Add To Basket"
        onPress={async () =>
          await logEvent(getAnalytics(), 'basket', {
            id: 3745092,
            item: 'mens grey t-shirt',
            description: ['round neck', 'long sleeved'],
            size: 'L',
          })
        }
      />
    </View>
  );
}
```

## Predefined Events

To help you get started, Analytics provides a number of [event methods](https://invertase.github.io/react-native-firebase/_react-native-firebase/analytics.html) that are common among
different types of apps, including retail and e-commerce, travel, and gaming apps. To learn more about these events and
when to use them, browse the [Events and properties](https://support.google.com/analytics/answer/9322688?hl=en&ref_topic=9267641)
articles in the Firebase Help Center.

Below is a sample of how to use one of the predefined methods the Analytics module provides for you:

```jsx
import react, { useEffect } from 'react';
import { View, Button } from 'react-native';
import { getAnalytics, logSelectContent } from '@react-native-firebase/analytics';

function App() {
  return (
    <View>
      <Button
        title="Press me"
        // Logs in the firebase analytics console as "select_content" event
        // only accepts the two object properties which accept strings.
        onPress={async () =>
          await logSelectContent(getAnalytics(), {
            content_type: 'clothing',
            item_id: 'abcd',
          })
        }
      />
    </View>
  );
}
```

For a full reference to predefined events and expected parameters, please check out the [reference API](https://invertase.github.io/react-native-firebase/_react-native-firebase/analytics.html).

## Reserved Events

The Analytics package works out of the box, however a number of events are automatically reported to Firebase.
These event names are called as 'Reserved Events'. Attempting to send any custom event using the `logEvent` method
with any of the following event names will throw an error.

| Reserved Events Names            |                                |                                 |
| -------------------------------- | ------------------------------ | ------------------------------- |
| `ad_activeview`                  | `ad_click`                     | `ad_exposure`                   |
| `ad_impression`                  | `ad_query`                     | `ad_reward`                     |
| `adunit_exposure`                | `app_background`               | `app_clear_data`                |
| `app_remove`                     | `app_store_refund`             | `app_store_subscription_cancel` |
| `app_store_subscription_convert` | `app_store_subscription_renew` | `app_update`                    |
| `app_upgrade`                    | `error`                        | `first_open`                    |
| `first_visit`                    | `in_app_purchase`              | `notification_dismiss`          |
| `notification_foreground`        | `notification_open`            | `notification_receive`          |
| `os_update`                      | `session_start`                | `session_start_with_rollout`    |
| `user_engagement`                |                                |                                 |

## App instance id

Below is an example showing how to retrieve the app instance id of the application. This will return null on android
if FirebaseAnalytics.ConsentType.ANALYTICS_STORAGE has been set to FirebaseAnalytics.ConsentStatus.DENIED and null on
iOS if ConsentType.analyticsStorage has been set to ConsentStatus.denied.

```jsx
import { getAnalytics, getAppInstanceId } from '@react-native-firebase/analytics';
// ...
const appInstanceId = await getAppInstanceId(getAnalytics());
```

### Web / Other platform instance id

Ensure you have installed an Async Storage provider for Firebase to preserve the instance id. Failure to do so means the instance id will be reset every time the application terminates.

The main documentation for "other platform" support contains [an example.](/platforms#async-storage)

# Disable Ad Id usage on iOS

Apple has a strict ban on the usage of Ad Ids ("IDFA") in Kids Category apps. They will not accept any app
in the Kids category if the app accesses the IDFA iOS symbols.

Additionally, apps must implement Apples "App Tracking Transparency" (or "ATT") requirements if they access IDFA symbols.
However, if an app does not use IDFA and otherwise handles data in an ATT-compatible way, it eliminates this ATT requirement.

If you need to avoid IDFA usage while still using analytics, then you need `firebase-ios-sdk` v7.11.0 or greater and to define the following variable in your Podfile:

```ruby
$RNFirebaseAnalyticsWithoutAdIdSupport = true
```

During `pod install`, using that variable installs the `FirebaseAnalytics/Core` Pod but not the `FirebaseAnalytics/IdentitySupport` Pod, so you may use Firebase Analytics in Kids Category apps,
or Firebase Analytics without needing the App Tracking Transparency handling (assuming no other parts
of your app handle data in a way that requires ATT)

Note that for obvious reasons, configuring Firebase Analytics for use without IDFA is incompatible with AdMob

# Google Analytics on-device conversion measurement

If you would like to enable Google Analytics on-device conversion measurement APIs on iOS, define the following variable in your Podfile:

```ruby
$RNFirebaseAnalyticsGoogleAppMeasurementOnDeviceConversion = true
```

During `pod install`, using that variable adds the `GoogleAdsOnDeviceConversion` Pod.

If you use Expo, including EAS Build, add the Analytics config plugin to your `app.json` / `app.config.js` instead of editing the generated Podfile manually:

```json
[
  "@react-native-firebase/analytics",
  {
    "ios": {
      "googleAppMeasurementOnDeviceConversion": true
    }
  }
]
```

This adds `$RNFirebaseAnalyticsGoogleAppMeasurementOnDeviceConversion = true` to the generated iOS `Podfile` during prebuild.

# Device Identification

If you would like to enable Firebase Analytics to generate automatic audience metrics for iOS (as it does by default in Android), you must link additional iOS libraries, [as documented by the Google Firebase team](https://support.google.com/firebase/answer/6318039). Specifically you need to link in `AdSupport.framework`.

The way to do this using CocoaPods is to add this variable to your `Podfile` so `@react-native-firebase/analytics` will link it in for you:

```ruby
$RNFirebaseAnalyticsEnableAdSupport = true
```

Note: this setting will have no effect if you disabled Ad IDs as described above, since this setting is specifically linking in the `AdSupport` framework which requires the Ad IDs.

# firebase.json

## Disable Auto-Initialization

Analytics can be further configured to disable auto collection of Analytics data. This is useful for opt-in-first
data flows, for example when dealing with GDPR compliance. This is possible by setting the below noted property
on the `firebase.json` file at the root of your project directory.

```json
// <project-root>/firebase.json
{
  "react-native": {
    "analytics_auto_collection_enabled": false
  }
}
```

To re-enable analytics (e.g. once you have the users consent), call the `setAnalyticsCollectionEnabled` method:

```js
import { getAnalytics, setAnalyticsCollectionEnabled } from '@react-native-firebase/analytics';
// ...
await setAnalyticsCollectionEnabled(getAnalytics(), true);
```

To update user's consent (e.g. once you have the users consent), call the `setConsent` method:

```js
import { getAnalytics, setConsent } from '@react-native-firebase/analytics';
// ...
await setConsent(getAnalytics(), {
  analytics_storage: true,
  ad_storage: true,
  ad_user_data: true,
  ad_personalization: true,
});
```

## Disable screenview tracking

Analytics automatically tracks some information about screens in your application, such as the class name of the UIViewController or Activity that is currently in focus.
Automatic screenview reporting can be turned off/on through `google_analytics_automatic_screen_reporting_enabled` property of `firebase.json` file.

```json
// <project-root>/firebase.json
{
  "react-native": {
    "google_analytics_automatic_screen_reporting_enabled": false
  }
}
```

# Seeing Events in Firebase Console Realtime View or Analytics DebugView

Events show in the Firebase Console Realtime View within a few seconds of your app sending the events. This is an easy way to verify your events implementation as you develop your application.

However, the Realtime View on Firebase Console offers no way to filter to a specific stream of events so after your app launches your development events will be mixed with all events from your app.

To examine just your development events you will need to use the Analytics DebugView available on the main Google Analytics site for your app as documented in the "Monitor the events in DebugView" section of the [Google Analytics documentation](https://support.google.com/analytics/answer/7201382)

Analytics events only show up in DebugView if marked correctly. Follow the instructions below for each platform to mark your events as Debug events.

## iOS

When running on iOS in debug, events won't be logged by default. If you want to see events in DebugView in the Firebase Console when running debug builds, you'll need to [first set a flag](https://firebase.google.com/docs/analytics/debugview#ios+) when launching in debug. This flag used to be variously called `-FIRAnalyticsDebugEnabled` and `-FIRDebugEnabled`, but please check the previous link.

To always set the flag when running debug builds of your app, you can [edit your scheme in Xcode](https://stackoverflow.com/questions/5025256/how-do-you-specify-command-line-arguments-in-xcode-4) to always include the flag.

## Android

When running on Android in debug, events won't be logged by default. If you want to see events in DebugView in the Firebase Console when running debug builds, you'll need to run the following command on the terminal `adb shell setprop debug.firebase.analytics.app <package-name>` - where `<package-name>` should be replaced with your app's package name.

## Other / Web

To mark your events as "Debug" events for platforms using react-native-firebase "other" platform support, you need to set the global debug flag `globalThis.RNFBDebug` to `true` then reload the app.

This toggle must be set to the value you want before accessing the analytics instance for the first time, so you should do it as early in your app's bootstrap sequence as possible.

For example, you might modify your index.js file like so:

```javascript
/**
 * @format
 */

import { AppRegistry } from 'react-native';
import App from './App';
import { name as appName } from './app.json';

//    \/  Add these lines below
// Enable debug mode for react-native-firebase:
if (__DEV__) globalThis.RNFBDebug = true;
//    /\  Add these lines above

AppRegistry.registerComponent(appName, () => App);
```
```

### Firebase JSON Config

Source: https://rnfirebase.io/app/json-config

```mdx

You can configure your installed modules by creating a file named `firebase.json` at the root of your project directory.
An example configuration file is available for inspection in our internal test app: [`<repo>/tests/firebase.json`](https://github.com/invertase/react-native-firebase/blob/main/tests/firebase.json)

## JSON Schema

Add the [Config Schema](https://github.com/invertase/react-native-firebase/blob/main/packages/app/firebase-schema.json) to your `firebase.json` file to use the Editor Intellisense

```json
{
  "$schema": "./node_modules/@react-native-firebase/app/firebase-schema.json"
}
```
```

### Core/App

Source: https://rnfirebase.io/app/usage

```mdx

The App module is available by default once you have installed the React Native Firebase library by following the
[Getting Started](/) documentation. The App module currently provides the following functionality:

- Creating [Secondary Firebase App Instances](/app/usage#secondary-apps).
- Exposing [Utilities](/app/utils) to aid development.

# Platform support and New Architecture

|                      |                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| **Platforms**        | Android, iOS (native Firebase SDK)                                                             |
| **New Architecture** | **Required** from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement). |

**Platform notes:** `initializeApp` is async on React Native (native bridge). `registerVersion` is **web only** and throws on native platforms.

# Secondary Apps

Unlike the Firebase Web SDK, there is no need to manually call the [`initializeApp`](https://firebase.google.com/docs/web/setup#add-sdks-initialize)
method with your project credentials. The native Android & iOS SDKs automatically connect to your Firebase project using
the credentials provided during the [Getting Started](/) installation steps. The app module does however provide support
for manually initializing secondary Firebase app instances.

Currently, the native Firebase SDKs only provide functionality for creating secondary apps on the following services:

- [App Check](/app-check/usage).
- [Authentication](/auth/usage).
- [Realtime Database](/database/usage).
- [Cloud Firestore](/firestore/usage).
- [Cloud Functions](/functions/usage).
- [Cloud Storage](/storage/usage).
- [ML](/ml/usage).
- [Installations](/installations/usage).
- [Remote Config](/remote-config/usage).

## Initializing secondary apps

The module exposes an `initializeApp` method which accepts arguments containing the credentials and options for your secondary
apps:

```js
import { initializeApp } from '@react-native-firebase/app';

// Your secondary Firebase project credentials...
const credentials = {
  clientId: '',
  appId: '',
  apiKey: '',
  databaseURL: '',
  storageBucket: '',
  messagingSenderId: '',
  projectId: '',
};

const config = {
  name: 'SECONDARY_APP',
};

await initializeApp(credentials, config);
```

Note that if you use multiple platforms, you will need to use the credentials relevant to that platform:

```js
import { initializeApp } from '@react-native-firebase/app';
import { Platform } from 'react-native';

// Your secondary Firebase project credentials for Android...
const androidCredentials = {
  clientId: '',
  appId: '',
  apiKey: '',
  databaseURL: '',
  storageBucket: '',
  messagingSenderId: '',
  projectId: '',
};

// Your secondary Firebase project credentials for iOS...
const iosCredentials = {
  clientId: '',
  appId: '',
  apiKey: '',
  databaseURL: '',
  storageBucket: '',
  messagingSenderId: '',
  projectId: '',
};

// Select the relevant credentials
const credentials = Platform.select({
  android: androidCredentials,
  ios: iosCredentials,
});

const config = {
  name: 'SECONDARY_APP',
};

await initializeApp(credentials, config);
```

Once created, you can confirm the app instance has been created by accessing the `apps` property on the module:

```js
import { getApps } from '@react-native-firebase/app';

const apps = getApps();

apps.forEach(app => {
  console.log('App name: ', app.name);
});
```

## Switching app instance

You can switch app instances at any time whilst developing by calling the `app` method with the name of the secondary app:

```js
import { getApp } from '@react-native-firebase/app';
import { getAuth } from '@react-native-firebase/auth';

getAuth(getApp('SECONDARY_APP')).currentUser;
```

Or pass the secondary app instance you created above directly to the desired module, for example:

```js
import { getApp, initializeApp } from '@react-native-firebase/app';
import { getAuth } from '@react-native-firebase/auth';

const secondaryApp = await initializeApp(credentials, config);

getAuth(secondaryApp).currentUser;
```

## Deleting instances

You can delete any secondary instances by calling the `delete` method on the instance:

```js
await getApp('SECONDARY_APP').delete();
```
```

### Utils

Source: https://rnfirebase.io/app/utils

```mdx

The App module also provides access to some handy utility methods which have been exposed to aid with your
development.

# File Paths

When working with modules such as [Cloud Storage](/storage), you may need to know about the devices
current directory paths. Rather than installing a separate module, the library provides useful statics
which can be used.

Access the `FilePath` static via utils:

```js
import { utils } from '@react-native-firebase/app';

console.log(utils.FilePath.PICTURES_DIRECTORY);
```

# Test Lab

Firebase [TestLab](https://firebase.google.com/docs/test-lab/?utm_source=invertase&utm_medium=react-native-firebase&utm_campaign=utils)
is a cloud-based app-testing infrastructure. With one operation, you can test your Android or iOS app across
a wide variety of devices and device configurations, and see the results—including logs, videos,
and screenshots—in the Firebase console.

It is useful to change the apps configuration if it is being run in Test Lab, for example disabling Analytics
data collection. Such functionality can be carried out by taking advantage of the `isRunningInTestLab`.

> Be aware, `isRunningInTestLab` is Android only property!

```js
import { utils } from '@react-native-firebase/app';
import { getAnalytics, setAnalyticsCollectionEnabled } from '@react-native-firebase/analytics';

async function bootstrap() {
  if (utils().isRunningInTestLab) {
    await setAnalyticsCollectionEnabled(getAnalytics(), false);
  }
}
```

# App Version

`appVersion` returns the host app's marketing version string: Android `versionName` or iOS
`CFBundleShortVersionString`. Use it when you need the installed app version without pulling in a
separate package.

> Be aware, `appVersion` is available on Android and iOS only. On web and other non-native platforms
> it is `undefined` (including when the native value is empty).

```js
import { utils } from '@react-native-firebase/app';

const version = utils().appVersion;
if (version) {
  console.log('App version:', version);
}
```

# Android - Checking Play Services

It is useful to know if the Android device has play services available, and what to do in response to certain use cases:

```js
import { utils } from '@react-native-firebase/app';

async function checkPlayServicesExample() {
  const { status, isAvailable, hasResolution, isUserResolvableError } =
    utils().playServicesAvailability;
  // all good and valid \o/
  if (isAvailable) return Promise.resolve();
  // if the user can resolve the issue i.e by updating play services
  // then call Google Play's own/default prompting functionality
  if (isUserResolvableError || hasResolution) {
    switch (status) {
      case 1:
        // SERVICE_MISSING - Google Play services is missing on this device.
        // show something to user
        // and then attempt to install if necessary
        return utils().makePlayServicesAvailable();
      case 2:
        // SERVICE_VERSION_UPDATE_REQUIRED - The installed version of Google Play services is out of date.
        // show something to user
        // and then attempt to update if necessary
        return utils().resolutionForPlayServices();

      default:
        // some default dialog / component?
        // use the link below to tailor response to status codes to suit your use case
        // https://developers.google.com/android/reference/com/google/android/gms/common/ConnectionResult#SERVICE_VERSION_UPDATE_REQUIRED
        if (isUserResolvableError) return utils().promptForPlayServices();
        if (hasResolution) return utils().resolutionForPlayServices();
    }
  }
  // There's no way to resolve play services on this device
  // probably best to show a dialog / force crash the app
  return Promise.reject(new Error('Unable to find a valid play services version.'));
}
```
```

### App Check

Source: https://rnfirebase.io/app-check/usage

```mdx

# Installation

This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app"
module, view the [Getting Started](/) documentation.

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the app-check module
yarn add @react-native-firebase/app-check

# If you're developing your app using iOS, run this command
cd ios/ && pod install
```

App Check requires you set the minimum iOS Deployment version in `ios/Podfile` to `11.0` or greater.

You may have Xcode compiler errors after including the App Check module, specifically referencing linker problems and missing directories.

You may find excluding the `i386` architecture via an addition to the `ios/Podfile` `post_install` hook like the below works:

```ruby
    installer.aggregate_targets.each do |aggregate_target|
      aggregate_target.user_project.native_targets.each do |target|
        target.build_configurations.each do |config|
          config.build_settings['ONLY_ACTIVE_ARCH'] = 'NO'
          config.build_settings['EXCLUDED_ARCHS'] = 'i386'
        end
      end
      aggregate_target.user_project.save
    end
```

# Platform support and New Architecture

|                      |                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| **Platforms**        | Android, iOS (native Firebase SDK)                                                             |
| **New Architecture** | **Required** from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement). |

**Platform notes:** Use `ReactNativeFirebaseAppCheckProvider` for native attestation (Device Check, App Attest, Play Integrity). firebase-js-sdk `ReCaptchaEnterpriseProvider` and `ReCaptchaV3Provider` are **web only**.

# What does it do

App Check works alongside other Firebase services to help protect your backend resources from abuse, such as billing fraud or phishing. With App Check, devices running your app will use an app or device attestation provider that attests to one or both of the following:

- Requests originate from your authentic app
- Requests originate from an authentic, untampered device

This attestation is attached to every request your app makes to your Firebase backend resources.

<YouTube id="Fjj4fmr2t04" />

This App Check module has built-in support for using the following services as attestation providers:

- DeviceCheck on iOS
- App Attest on iOS
- Play Integrity on Android (requires distribution from Play Store to successfully fetch tokens)
- SafetyNet on Android (deprecated)
- Debug providers on both platforms

App Check currently works with the following Firebase products:

- Realtime Database
- Cloud Firestore
- Cloud Storage
- Cloud Functions (callable functions)

The [official Firebase App Check documentation](https://firebase.google.com/docs/app-check) has more information, including about the iOS AppAttest provider, and testing/ CI integration, it is worth a read.

# Usage

## Register Firebase Apps

Before the App Check package can be used on iOS or Android, the corresponding App must be registered in the firebase console.

For instructions on how to generate required keys and register an app for the desired attestation provider, follow **Step 1** in these firebase guides:

- [Get started using App Check with DeviceCheck on Apple platforms](https://firebase.google.com/docs/app-check/ios/devicecheck-provider#project-setup)
- [Get started using App Check with App Attest on Apple platforms](https://firebase.google.com/docs/app-check/ios/app-attest-provider#project-setup)
- [Get started using App Check with Play Integrity on Android](https://firebase.google.com/docs/app-check/android/play-integrity-provider#project-setup)
- [Get started using App Check with SafetyNet on Android (deprecated)](https://firebase.google.com/docs/app-check/android/safetynet-provider#project-setup)

> Additionally, You can reference the iOS private key creation and registrations steps outlined in the [Cloud Messaging iOS Setup](/messaging/usage/ios-setup#linking-apns-with-fcm-ios).

## Initialize

> If you're using Expo Managed Workflow, you can load the `@react-native-firebase/app-check` config plugin to skip the native setup step below. The plugin only registers the native module before `FirebaseApp.configure()` (Firebase requires this order regardless of which provider you use); you still need to call `initializeAppCheck` from JavaScript.

You must call `initializeAppCheck` prior to calling any Firebase backend services for App Check to function. Until `initializeAppCheck` (or the deprecated `activate`) configures a provider, App Check is pending: any `getToken` / `getLimitedUseToken` call fails immediately with `appCheck/provider-not-ready` instead of fetching a real token.

#### All react-native >= 0.77 with AppDelegate.swift

The AppCheck pod does not expose a Swift-importable module surface (its C++ codegen headers stay private), so importing it directly in a Swift file does not work.
Edit the bridging header at `<your project name>/ios/<your project-name>-Bridging-Header.h`

You will need to add the line indicated in the example below:

```diff
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

+ #import "RNFBAppCheckModule.h" // <-- new for AppCheck to work
```

After doing that, follow the instructions below to add AppCheck initialization to your `AppDelegate.swift` file depending on the react-native version you use.

#### Configure AppCheck with iOS credentials (react-native 0.79+)

To do that, edit your `ios/ProjectName/AppDelegate.swift` and add the following two lines:

At the top of the file, import the FirebaseCore SDK right after `import UIKit`:
And within your existing `didFinishLaunchingWithOptions` method, add the following to the top of the method:

```diff
import UIKit
+ import FirebaseCore  // <-- From App/Core integration, no other Firebase items needed
import React
import React_RCTAppDelegate
import ReactAppDependencyProvider

...

  func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {
+   RNFBAppCheckModule.sharedInstance()  // <-- new for AppCheck to work
+   FirebaseApp.configure()              // <-- From App/Core integration
```

#### Configure AppCheck with iOS credentials (react-native >= 0.77 && < 0.79)

To do that, edit your `ios/ProjectName/AppDelegate.swift` and add the following two lines:

At the top of the file, import the FirebaseCore SDK right after `import UIKit`:
And within your existing `didFinishLaunchingWithOptions` method, add the following to the top of the method:

```diff
import UIKit
+ import FirebaseCore  // <-- From App/Core integration, no other Firebase items needed
import React
import React_RCTAppDelegate
import ReactAppDependencyProvider

@main
class AppDelegate: RCTAppDelegate {
  override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
+   RNFBAppCheckModule.sharedInstance()  // <-- new for AppCheck to work
+   FirebaseApp.configure()              // <-- From App/Core integration
```

> **Note:** Do not add `import RNFBAppCheck` in Swift. The AppCheck pod is Obj-C only and does not produce a Swift module. Use the bridging header import above instead.

#### Configure Firebase with iOS credentials (react-native < 0.77)

To do that, edit your `ios/ProjectName/AppDelegate.mm` and add the following two lines:

```objectivec
#import "AppDelegate.h"
#import "RNFBAppCheckModule.h" // ⬅️ ADD THIS LINE
#import <Firebase.h>
...

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  // Initialize RNFBAppCheckModule, it sets the custom RNFBAppCheckProviderFactory
  // which lets us configure any of the available native platform providers,
  // and reconfigure if needed, dynamically after `[FIRApp configure]` just like the other platforms.

  [RNFBAppCheckModule sharedInstance]; // ⬅️ ADD THIS LINE BEFORE [FIRApp configure]

  [FIRApp configure];

  ...
}

```

There are several differences between the web, Apple, and Android platform SDKs produced by Firebase, which react-native-firebase smooths over to give you a common, firebase-js-sdk compatible API.

How do we do this? We use the standard firebase-js-sdk v9 API `initializeAppCheck`, and take advantage of its parameters which allow the use of an `AppCheckOptions` argument that itself allows a `CustomProvider`.

It is through the use of a react-native-specific `ReactNativeFirebaseAppCheckProvider` that we can offer runtime configuration capability at the javascript level, including the ability to switch providers dynamically.

So AppCheck module initialization is done in two steps in react-native-firebase - first you create and configure the custom provider, then you initialize AppCheck using that custom provider.

Starting in v25, the modular App Check helpers and types are exported from `@react-native-firebase/app-check` at the package root to better match the Firebase JS SDK. For example, import `initializeAppCheck`, `AppCheck`, and `AppCheckTokenResult` directly from `@react-native-firebase/app-check` when using the modular API.

### Configure a Custom Provider

To configure the react-native-firebase custom provider, first obtain one, then configure it according to the providers you want to use on each platform.

```javascript
import { ReactNativeFirebaseAppCheckProvider } from '@react-native-firebase/app-check';

const rnfbProvider = new ReactNativeFirebaseAppCheckProvider();
rnfbProvider.configure({
  android: {
    provider: __DEV__ ? 'debug' : 'playIntegrity',
    debugToken: 'some token you have configured for your project firebase web console',
  },
  apple: {
    provider: __DEV__ ? 'debug' : 'appAttestWithDeviceCheckFallback',
    debugToken: 'some token you have configured for your project firebase web console',
  },
  web: {
    provider: 'reCaptchaV3',
    siteKey: 'unknown',
  },
});
```

### Install the Custom Provider

Once you have the custom provider configured, install it in app-check using the firebase-js-sdk compatible API, while saving the returned instance for usage:

```javascript
import { getApp } from '@react-native-firebase/app';
import { initializeAppCheck } from '@react-native-firebase/app-check';

const appCheck = await initializeAppCheck(getApp(), {
  provider: rnfbProvider,
  isTokenAutoRefreshEnabled: true,
});
```

### Inline Provider Configuration

If you do not need to keep a provider instance around, you can pass the React Native provider configuration inline using `providerOptions`:

```javascript
import { getApp } from '@react-native-firebase/app';
import { initializeAppCheck } from '@react-native-firebase/app-check';

const appCheck = await initializeAppCheck(getApp(), {
  provider: {
    providerOptions: {
      android: {
        provider: __DEV__ ? 'debug' : 'playIntegrity',
        debugToken: 'some token you have configured for your project firebase web console',
      },
      apple: {
        provider: __DEV__ ? 'debug' : 'appAttestWithDeviceCheckFallback',
        debugToken: 'some token you have configured for your project firebase web console',
      },
      web: {
        provider: 'reCaptchaV3',
        siteKey: 'unknown',
      },
    },
  },
  isTokenAutoRefreshEnabled: true,
});
```

### Verify AppCheck was initialized correctly

After initializing the custom provider, you can verify AppCheck is working by logging a response from the token server:

```javascript
import { getToken } from '@react-native-firebase/app-check';

try {
  // `appCheckInstance` is the saved return value from initializeAppCheck
  const { token } = await appCheckInstance.getToken(true);

  if (token.length > 0) {
    console.log('AppCheck verification passed');
  }
} catch (error) {
  console.log('AppCheck verification failed');
}
```

### Listening for token changes

On Android, you can subscribe to App Check token updates with `onTokenChanged`. On iOS this listener is not implemented yet – subscribing will no-op and log a warning. If you need to react to token changes on iOS, prefer polling `getToken` on demand or rely on automatic refresh.

```javascript
import { onTokenChanged, getToken } from '@react-native-firebase/app-check';

// Android: receives updates. iOS: no-op (native API does not exist on the SDK).
const unsubscribe = onTokenChanged(appCheckInstance, async ({ token }) => {
  console.log('App Check token updated:', token);
});

// iOS-friendly approach: request a fresh token when needed
const { token } = await appCheckInstance.getToken(true);
```

## Automatic Data Collection

App Check has an "tokenAutoRefreshEnabled" setting. This may cause App Check to attempt a remote App Check token fetch prior to user consent. In certain scenarios, like those that exist in GDPR-compliant apps running for the first time, this may be unwanted.

You may configure this setting in `firebase.json` such that your desired configuration is in place even before you the react-native javascript bundle begins executing and allows for runtime configuration.

If unset, the "tokenAutoRefreshEnabled" setting will defer to the app's "automatic data collection" setting, which may be set in `firebase.json`, or if you wish directly in the Info.plist or AndroidManifest.xml according to the Firebase native SDK documentation. Unless otherwise configured, it will default to true implying there will be automatic data collection and app check token refresh attempts.

## Using App Check tokens for non-firebase services

The [official documentation](https://firebase.google.com/docs/app-check/web/custom-resource) shows how to use `getToken` to access the current App Check token and then verify it in external services.

## Troubleshooting

### `appCheck/provider-not-ready`

If `getToken` / `getLimitedUseToken` rejects with `appCheck/provider-not-ready`, App Check has not been configured yet for that app. Call `initializeAppCheck` (or the deprecated `activate`) before requesting tokens or calling any App Check-protected Firebase service.

This error replaces an older iOS behavior where App Check could install the debug provider before `initializeAppCheck` ran, including in release builds. That could exchange a debug token with Firebase's backend outside of development, showing up as unexplained `exchangeDebugToken` 403/429 responses in your logs. If you saw those errors before upgrading, look for a code path that requests a token before `initializeAppCheck` completes and move it after.

## Manually Setting Up App Check Debug Token for Testing Environments / CI

### on iOS

The react-native-firebase CustomProvider implementation allows for runtime configuration of the `debug` provider as well as a `debugToken` in the `ios` CustomProvider options. This allows the easy use of a token pre-configured in the Firebase console, allowing for dynamic configuration and testing of AppCheck in CI environments or iOS Simulators.

### on Android

The react-native-firebase CustomProvider implementation allows for runtime configuration of the `debug` provider as well as a `debugToken` in the `android` CustomProvider options. This allows the easy use of a token pre-configured in the Firebase console, allowing for dynamic configuration and testing of AppCheck in CI environments or Android Emulators.

There are a variety of other ways to obtain and configure debug tokens for AppCheck testing, a few of which follow:

#### A) When testing on an actual android device (debug build)

1.  Start your application on the android device.
2.  Use `$adb logcat | grep DebugAppCheckProvider` to grab your temporary secret from the android logs. The output should look lit this:

        D DebugAppCheckProvider: Enter this debug secret into the allow list in
        the Firebase Console for your project: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

3.  In the [Project Settings > App Check](https://console.firebase.google.com/project/_/settings/appcheck) section of the Firebase console, choose _Manage debug tokens_ from your app's overflow menu. Then, register the debug token you logged in the previous step.

#### B) Specifying a generated `FIREBASE_APP_CHECK_DEBUG_TOKEN` -- building for CI/CD (debug build)

When you want to test using an Android virtual device -or- when you prefer to (re)use a token of your choice -- e.g. when configuring a CI/CD pipeline -- use the following steps:

1.  In the [Project Settings > App Check](https://console.firebase.google.com/project/_/settings/appcheck) section of the Firebase console, choose _Manage debug tokens_ from your app's overflow menu. Then, register a new debug token by clicking the _Add debug token_ button, then _Generate token_.
2.  Pass the token you created in the previous step by supplying a `FIREBASE_APP_CHECK_DEBUG_TOKEN` environment variable to the process that build your react-native android app. e.g.:

        FIREBASE_APP_CHECK_DEBUG_TOKEN="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" react-native run-android

Please note that once the android app has successfully passed the app-checks controls on the device, it will keep passing them, whether you rebuild without the secret token or not. To completely reset app-check, you must first uninstall, and then re-build / install.

#### C) When using Expo Development Client

When using expo-dev-client, the process is a little different, especially on an android emulator.

1. In the [Project Settings > App Check](https://console.firebase.google.com/project/_/settings/appcheck) section of the Firebase console, choose _Manage debug tokens_ from your app's overflow menu. Then, register a new debug token by clicking the _Add debug token_ button, then _Generate token_.
2. Pass the token you created in the previous step by supplying a `FIREBASE_APP_CHECK_DEBUG_TOKEN` environment variable in your eas.json development profile:

```json
{
  ...
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal",
      "env": {
        ...
        "FIREBASE_APP_CHECK_DEBUG_TOKEN": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
      }
    },
    ...
  },
  ...
}
```

3.  Rebuild your development client:

        eas build --profile development --platform android
```

### App Distribution

Source: https://rnfirebase.io/app-distribution/usage

```mdx

# Installation

This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app"
module, view the [Getting Started](/) documentation.

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the app-distribution module
yarn add @react-native-firebase/app-distribution

# If you're developing your app using iOS, run this command
cd ios/ && pod install
```

## Add the App Distribution Plugin

> If you're using Expo, make sure to add the `@react-native-firebase/app-distribution` config plugin to your `app.json` or `app.config.js`. It handles the below installation steps for you. For instructions on how to do that, view the [Expo](/#expo) installation section.

On Android, you need to install the Google App Distribution Plugin.

Add the plugin to your `/android/build.gradle` file as a dependency:

```groovy
buildscript {
    dependencies {
        // ...
        classpath 'com.google.firebase:firebase-appdistribution-gradle:5.3.0'
    }
```

Apply the plugin via the `/android/app/build.gradle` file (at the top):

```groovy
apply plugin: 'com.android.application'
apply plugin: 'com.google.firebase.appdistribution'
```

# Platform support and New Architecture

|                      |                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| **Platforms**        | Android, iOS (native Firebase SDK)                                                             |
| **New Architecture** | **Required** from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement). |

> **React Native only:** There is no firebase-js-sdk web equivalent for App Distribution.

# What does it do

Firebase App Distribution gives a holistic view of your beta testing program across iOS and Android, providing you with valuable feedback before a new release is in production. You can send pre-release versions of your app using the console or your CI servers, and installing your app is easy for testers.

Firebase App Distribution makes distributing your apps to trusted testers painless. By getting your apps onto testers' devices quickly, you can get feedback early and often. And if you use Crashlytics in your apps, you’ll automatically get stability metrics for all your builds, so you know when you’re ready to ship.

<YouTube id="SiPOaV-5j9o" />

# Key capabilities

- Cross-platform Manage both your iOS and Android pre-release distributions from the same place.
- Fast distributions Get early releases into your testers' hands quickly, with fast onboarding, no SDK to install, and instant app delivery.
- Fits into your workflow Distribute builds using the Firebase console, the Firebase Command Line Interface (CLI) tool, - or Gradle (Android). Automate distribution by integrating the CLI into CI jobs.
- Tester management Manage your testing teams by organizing them into groups. Easily add new testers with email invitations that walk them through the onboarding process. See the status of each tester for specific versions of your app: view who has accepted a testing invitation and downloaded the app.
- Works with Android App Bundles Distribute releases to testers for your Android App Bundle in Google Play. App - Distribution integrates with Google Play's internal app sharing service to streamline your app testing and launching processes.
- Works with Crashlytics When combined with Crashlytics, get insights into the stability of your test distributions.

The [official Firebase App Check documentation](https://firebase.google.com/docs/app-distribution) has more information, including about build upload integration, it is worth a read.

# Usage

The react-native-firebase module for App Distribution is meant to expose the new version alert capabilities of the iOS SDK. The majority of the App Distribution Firebase service depends on native build/release integrations. Those build/release integrations must be natively implemented for iOS and Android, according to [the upstream docs from Firebase](https://firebase.google.com/docs/app-distribution) or our build system provider.

## New Version Alerts

On iOS if you include the App Distribution module, you can optionally enable in-app alerts that appear when new builds are available to test.

## Tester Sign-in Status

The methods signInTester and isTesterSignedIn give you more flexibility customizing your tester's sign-in experience, so it can better match your app's look and feel.

You may check if your tester has already signed into their Firebase App Distribution tester account, so you can choose to display your sign-in UI only for testers who haven't yet signed in. After the tester has signed in, you can then call checkForUpdate to check whether the tester has access to a new build.

##
```

### Email Link Authentication

Source: https://rnfirebase.io/auth/email-link-auth

```mdx

Email link (passwordless) authentication sends the user a one-time sign-in link. Opening that link on a device completes
sign-in and verifies ownership of the email address — no password required.

Firebase Auth no longer relies on [Firebase Dynamic Links](https://firebase.google.com/support/dynamic-links-faq) for this
flow. Mobile apps complete sign-in through **Android App Links** and **iOS Universal Links** on a domain you control
(often a Firebase Hosting domain). Official platform guides:

- [Android](https://firebase.google.com/docs/auth/android/email-link-auth)
- [Apple platforms](https://firebase.google.com/docs/auth/ios/email-link-auth)
- [Migration away from Dynamic Links](https://firebase.google.com/docs/auth/android/email-link-migration)

This page focuses on wiring the flow in React Native Firebase. It starts with the same default Hosting-domain path as the
official Android and Apple guides. There is information on optionally adding custom domains and branded landing pages later.

# How the pieces fit together

| Piece                                       | Role                                                                                                                      |
| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| **Continue URL** (`ActionCodeSettings.url`) | HTTPS URL embedded in the email. Its **domain must be an Authorized domain** in the Firebase Console.                     |
| **Hosting link domain**                     | Domain Firebase uses for the mobile Auth link (default `PROJECT_ID.firebaseapp.com`, or a custom Hosting / `linkDomain`). |
| **Association files**                       | `/.well-known/assetlinks.json` (Android) and `/.well-known/apple-app-site-association` (iOS) so the OS opens your app.    |
| **Native app config**                       | Android `intent-filter` + SHA certificates; iOS Associated Domains + `Linking` handlers.                                  |
| **JS Auth APIs**                            | `sendSignInLinkToEmail` → store email → `isSignInWithEmailLink` → `signInWithEmailLink`.                                  |

`handleCodeInApp` must be `true` for email-link sign-in. Do **not** set deprecated `dynamicLinkDomain`. Prefer omitting
`linkDomain` unless you have configured a **custom** Hosting link domain for the project — Auth selects the project default
Hosting domain when it is omitted. Setting `linkDomain` to a bare `*.web.app` / `*.firebaseapp.com` default is often rejected.

## Migrate from Dynamic Links

If the project previously used Firebase Dynamic Links for email sign-in, run the Admin SDK project-config update from the
[Android migration guide](https://firebase.google.com/docs/auth/android/email-link-migration) so Auth issues Hosting mobile
links instead of FDL:

```js
import { getAuth } from 'firebase-admin/auth';

await getAuth()
  .projectConfigManager()
  .updateProjectConfig({
    mobileLinksConfig: {
      // Literal value from the Firebase migration docs — switches the project
      // from Dynamic Links to Firebase Hosting mobile links.
      domain: 'HOSTING_DOMAIN',
    },
  });
```

If emails still contain `*.page.link` after this, the project is still using Firebase Dynamic Links. `linkDomain` in `ActionCodeSettings` is only
for a **custom** Hosting domain — it does **not** perform this migration switch. Omitting deprecated `dynamicLinkDomain` is
required; see Troubleshooting if you still see `DYNAMIC_LINK_NOT_ACTIVATED`.

# Choose a hosting path

You need a domain that can receive `/__/auth/links` (App Links / Universal Links). Pick the simplest path that matches your
app:

| Situation                              | Use                                                                                                                                                                                                                                                  |
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Getting started** (no custom domain) | Use the project's default Hosting domain `PROJECT_ID.firebaseapp.com`. Register Android SHA-1 + SHA-256 in Firebase project settings so Firebase can serve association files.                                                                        |
| **Custom / branded domain**            | Attach a custom domain in Firebase Hosting (or reuse a domain you already operate). Serve `/.well-known/assetlinks.json` and `/.well-known/apple-app-site-association` on that host. Set `linkDomain` in `ActionCodeSettings` to that custom domain. |

Both paths end the same way: Authorized domain in Firebase Auth, association files available on the Hosting link domain, and
the React Native app claiming that domain via App Links / Universal Links.

## Association files (custom domain only)

On the **default** `PROJECT_ID.firebaseapp.com` path, Firebase serves association files after SHA fingerprints are
registered — no separate generator required.

For a **custom** domain, publish these at the Hosting / custom domain (inline JSON, not a file download):

- `https://YOUR_DOMAIN/.well-known/assetlinks.json`
- `https://YOUR_DOMAIN/.well-known/apple-app-site-association`

`assetlinks.json` needs `package_name` + SHA-256 fingerprints ([Digital Asset Links](https://developers.google.com/digital-asset-links/v1/getting-started)).
AASA needs your Apple Team ID + bundle ID and paths covering `/__/auth/links*`
([Supporting associated domains](https://developer.apple.com/documentation/xcode/supporting-associated-domains)). Serve both
with `Content-Type: application/json` and no attachment disposition.

Community generators exist for these JSON files; they are optional and unofficial — see
[Optional community helpers](#optional-community-helpers) at the end of this page.

# Firebase Console setup

1. Enable **Email/Password**, then enable **Email link (passwordless sign-in)** under
   [Authentication → Sign-in method](https://console.firebase.google.com/project/_/authentication/providers).
2. Add your continue-URL host under **Authentication → Settings → Authorized domains**.
3. Android: in Project settings, register the app's package name plus **SHA-1 and SHA-256** certificate fingerprints
   (required for App Links verification on the default Hosting domain).
4. iOS: ensure the bundle ID in the Firebase iOS app matches Xcode / Associated Domains.

# Native app setup

Two domains are involved — confusing them is a common reason the link opens Safari or Chrome instead of the app:

1. **Hosting link domain** (must match Associated Domains / `autoVerify` intent-filter): `https://PROJECT_ID.firebaseapp.com/__/auth/links?...`
   — this is the URL the OS must open in the app.
2. **Continue URL** (`ActionCodeSettings.url`): any authorized HTTPS URL; used when the app is not installed and as embedded
   state. It does **not** have to be the same host as (1).

Claim the **Hosting link domain** in native config — typically `PROJECT_ID.firebaseapp.com`. Add `PROJECT_ID.web.app` or a
custom Hosting domain only if your project uses that host for Auth links.

## Android

Add an `autoVerify` intent-filter on the activity that should receive the link (`launchMode="singleTask"` is typical so a
cold/warm start reuses one activity). Prefer **separate** `<data>` tags for scheme, host, and path — Android merges siblings.
See the [upstream Android intent-filter](https://firebase.google.com/docs/auth/android/email-link-auth#completing-sign-in-in-an-android-app).

```xml
<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="https" />
  <data android:host="YOUR_PROJECT_ID.firebaseapp.com" />
  <data android:pathPrefix="/__/auth/links" />
</intent-filter>
```

If you use a custom Hosting link domain, add another `<data android:host="…" />` for that host. If your branded continue URL
is the site root (or other paths), add another filter (or additional path rules) for those hosts.

After install, verify App Links with:

```bash
adb shell pm get-app-links YOUR.PACKAGE.NAME
# optional force re-verify
adb shell pm verify-app-links --re-verify YOUR.PACKAGE.NAME
```

## iOS

1. Enable the **Associated Domains** capability and add `applinks:YOUR_PROJECT_ID.firebaseapp.com`. Add `applinks:YOUR_PROJECT_ID.web.app`
   or your custom Hosting domain only if Auth links use that host.

2. Forward Universal Links and custom URLs to React Native `Linking` (React Native's `RCTLinkingManager`), for example in
   `AppDelegate`:

```swift
func application(
  _ application: UIApplication,
  continue userActivity: NSUserActivity,
  restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
) -> Bool {
  return RCTLinkingManager.application(
    application,
    continue: userActivity,
    restorationHandler: restorationHandler
  )
}

func application(
  _ app: UIApplication,
  open url: URL,
  options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
  return RCTLinkingManager.application(app, open: url, options: options)
}
```

On a Mac with Apple's Shared Web Credentials tooling you can sanity-check AASA matching:

```bash
sudo swcutil verify -d YOUR_DOMAIN -j ./apple-app-site-association -u https://YOUR_DOMAIN/__/auth/links
```

# Expo Setup

Email-link auth needs native App Links / Universal Links, so a [development build](https://docs.expo.dev/develop/development-builds/introduction/)
is required (Expo Go cannot claim `applinks:`).

In `app.json` / `app.config.js`:

- iOS: `expo.ios.associatedDomains`: `["applinks:YOUR_PROJECT_ID.firebaseapp.com"]`
- Android: an `intentFilters` entry with `autoVerify: true`, `https`, host `YOUR_PROJECT_ID.firebaseapp.com`, `pathPrefix`
  `/__/auth/links`
- Keep `@react-native-firebase/app` (and auth) config plugins as in the [Expo install docs](/#installation-for-expo-projects)

JS handling is unchanged: React Native `Linking.getInitialURL()` + `Linking.addEventListener('url')`. For more info on using
Expo with React Native Firebase, see our [Expo docs](/#installation-for-expo-projects).

# JavaScript / React Native flow

Ensure `@react-native-firebase/app` and `@react-native-firebase/auth` are installed (see [Authentication usage](/auth/usage)).

## 1. Send the link

Persist the email locally (for example AsyncStorage) **before or immediately after** sending — completing sign-in requires
the same address, and you must not put it in the continue URL query string.

```js
import AsyncStorage from '@react-native-async-storage/async-storage';
import { getAuth, sendSignInLinkToEmail } from '@react-native-firebase/auth';

const CONTINUE_URL = 'https://YOUR_PROJECT_ID.firebaseapp.com'; // Authorized domain
const EMAIL_KEY = 'emailForSignIn';

async function sendSignInLink(email) {
  await sendSignInLinkToEmail(getAuth(), email, {
    url: CONTINUE_URL,
    handleCodeInApp: true,
    // Omit linkDomain unless you configured a custom Hosting link domain.
    iOS: {
      bundleId: 'com.yourcompany.yourapp',
    },
    android: {
      packageName: 'com.yourcompany.yourapp',
      installApp: true,
      minimumVersion: '1',
    },
  });

  await AsyncStorage.setItem(EMAIL_KEY, email);
}
```

Modular `sendSignInLinkToEmail` **requires** `actionCodeSettings` (unlike the legacy namespaced helper defaults). See
[Migrating to v25](/migrating-to-v25) / [v26](/migrating-to-v26) for API notes.

## 2. Listen for the incoming URL

Use React Native `Linking` for both cold start (`getInitialURL`) and warm start (`url` events):

```js
import { useEffect } from 'react';
import { Linking } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { getAuth, isSignInWithEmailLink, signInWithEmailLink } from '@react-native-firebase/auth';

const EMAIL_KEY = 'emailForSignIn';

async function completeFromUrl(url) {
  if (!url || !isSignInWithEmailLink(getAuth(), url)) {
    return;
  }

  const email = await AsyncStorage.getItem(EMAIL_KEY);
  if (!email) {
    // Same device: shouldn't happen. Other device / cleared storage:
    // prompt the user to type the address the link was sent to, then continue.
    return;
  }

  try {
    await signInWithEmailLink(getAuth(), email, url);
    await AsyncStorage.removeItem(EMAIL_KEY);
  } catch (error) {
    // Expired or reused links commonly surface as auth/invalid-action-code.
    console.error(error.code, error.message);
  }
}

useEffect(() => {
  Linking.getInitialURL().then(completeFromUrl);
  const sub = Linking.addEventListener('url', ({ url }) => {
    completeFromUrl(url);
  });
  return () => sub.remove();
}, []);
```

From React Native Firebase **v26**, `isSignInWithEmailLink` returns a synchronous `boolean` (matching firebase-js-sdk). Do
not `await` it. On v25 it returned `Promise<boolean>` — see [Migrating to v26](/migrating-to-v26).

After a successful `signInWithEmailLink`, any [`onAuthStateChanged`](/auth/usage#listening-to-authentication-state) listeners
receive the signed-in user.

## 3. Different device / missing stored email

If the user opens the link on another device, ask them to type the email again, then call `signInWithEmailLink` with that
address and the incoming URL. Firebase requires the email to match the address the link was sent to.

## 4. Link / re-authenticate with an email link

To **link** an email-link credential to an existing signed-in user, or to **re-authenticate**, build a credential from the
same incoming URL and call `linkWithCredential` / `reauthenticateWithCredential`:

```js
import {
  EmailAuthProvider,
  getAuth,
  linkWithCredential,
  reauthenticateWithCredential,
} from '@react-native-firebase/auth';

const user = getAuth().currentUser;
if (user) {
  const credential = EmailAuthProvider.credentialWithLink(email, url);
  await linkWithCredential(user, credential);
  // or: await reauthenticateWithCredential(user, credential);
}
```

URL handling (App Links / Universal Links + `isSignInWithEmailLink`) is the same as for sign-in.

## 5. Email enumeration protection

Projects created after **2023-09-15** enable [email enumeration protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection)
by default. Do **not** rely on `fetchSignInMethodsForEmail` for identifier-first UI when that protection is on — see the note
on Firebase's [Android](https://firebase.google.com/docs/auth/android/email-link-auth) and
[iOS](https://firebase.google.com/docs/auth/ios/email-link-auth) email-link pages.

# Checklist

1. Email/Password + Email link providers enabled.
2. Continue URL domain is **Authorized**.
3. Default path: Android SHA fingerprints registered so Firebase can serve association files — or, for a custom domain,
   association files live at `/.well-known/` and return JSON inline.
4. Android intent-filter uses `autoVerify` and your Hosting link host(s) (including `/__/auth/links`).
5. iOS Associated Domains match the host(s); `RCTLinkingManager` receives Universal Links.
6. App stores the email, sends the link with `handleCodeInApp: true`, then completes with `signInWithEmailLink`.
7. Test on a **physical device** (App Links / Universal Links verification is unreliable or limited on many emulators/simulators).

# Troubleshooting

| Symptom                                                                        | What to check                                                                                                                                                                                                                                                                                                          |
| ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DYNAMIC_LINK_NOT_ACTIVATED` / email still uses `*.page.link`                  | Run Admin SDK `mobileLinksConfig.domain: 'HOSTING_DOMAIN'`. Do not set `dynamicLinkDomain`.                                                                                                                                                                                                                            |
| Link opens the browser, not the app                                            | Physical device; AASA/assetlinks 200 + `application/json`; intent-filter / `applinks:` host is the **Hosting link** domain (`PROJECT_ID.firebaseapp.com/__/auth/links`), not only the continue URL.                                                                                                                    |
| iOS: Firebase email URL doesn't open the app, but pasting the Hosting URL does | Custom SMTP (e.g. SendGrid) wrapping the Auth URL in a redirect. Universal Links do not follow that redirect into the app. Use Firebase's default email sender, or a custom SMTP that does **not** wrap the destination URL. Reported in [#8405](https://github.com/invertase/react-native-firebase/discussions/8405). |
| `auth/invalid-action-code`                                                     | Link expired or already used; send a new one.                                                                                                                                                                                                                                                                          |
| `auth/internal-error`                                                          | Check native logs (`adb logcat` / Console.app on a real device), not just the JS error.                                                                                                                                                                                                                                |

# Related

- [Authentication usage](/auth/usage)
- Firebase: [Android email link](https://firebase.google.com/docs/auth/android/email-link-auth) ·
  [iOS email link](https://firebase.google.com/docs/auth/ios/email-link-auth) ·
  [Migrate off Dynamic Links](https://firebase.google.com/docs/auth/android/email-link-migration)

# Optional community helpers

The [default Hosting path](#choose-a-hosting-path) above is the simplest integration for most apps. The projects below were
built by community contributors for setups that need more than that — a **custom domain**, **your brand in the sign-in
email** (instead of the default `firebaseapp.com` link), a branded continue page, or association files on a host you already
operate. That work may genuinely help when you have those requirements.

React Native Firebase does not maintain, control, or endorse these tools; we cannot vouch for their security or long-term
availability. Prefer the default Hosting path when it fits, and evaluate each project for your own requirements.

## No existing website — ready-made hosting helper

[email-link-host](https://github.com/rutvik24/email-link-host) is an optional community helper that provides a **ready-made
hosting solution** so you do not have to create association files and a continue-page host from scratch. It generates
`assetlinks.json` and `apple-app-site-association` from environment variables, serves a branded continue page, and can
deploy to Firebase Hosting, Cloudflare Pages, Vercel, Netlify, Amplify, or Docker.

- [Documentation](https://rutvik24.github.io/email-link-host/)
- [Live demo](https://fir-email-link-host.web.app)
- [Configuration reference](https://rutvik24.github.io/email-link-host/docs/email-link-host/configuration)

Set your Android package name, SHA-256 fingerprints, and Apple Team ID / bundle ID, then confirm
`/.well-known/assetlinks.json` and `/.well-known/apple-app-site-association` render as inline JSON (not a file download).

## Existing website — form-based file generator

If you already operate a website or Hosting domain, [App Universal Links Helper](https://rutvik24.github.io/app-universal-links-helper/)
(or [run locally](https://github.com/rutvik24/app-universal-links-helper)) is an optional community helper: fill in your hosts,
package name, fingerprints, and path rules (include `/__/auth/links` for Auth links) in a form, then download the generated
`assetlinks.json`, `apple-app-site-association`, AndroidManifest `<intent-filter>` snippet, and optional `adb` / `curl` /
`swcutil` verification commands. It does **not** upload files — publish the JSON under `/.well-known/` on your domain.

## End-to-end sample app

[firebase-email-link-host-demo](https://github.com/rutvik24/firebase-email-link-host-demo) is an optional community sample that
wires React Native modular Auth + App Links + Universal Links + AsyncStorage. Useful starting points:
`src/services/firebaseAuth.ts`, `useEmailLinkAuth.ts`, and the platform manifest / entitlements files.
```

### Multi-factor Auth

Source: https://rnfirebase.io/auth/multi-factor-auth

```mdx

> Before a user can enroll a second factor they need to verify their email. See
> [`User`](https://invertase.github.io/react-native-firebase/_react-native-firebase/auth/FirebaseAuthTypes/User.html#sendemailverification) interface is returned.

# TOTP MFA

The [official guide for Firebase web TOTP authentication](https://firebase.google.com/docs/auth/web/totp-mfa) explains the TOTP process well, including project prerequisites to enable the feature, as well as code examples.

The API details and usage examples may be combined with the full Phone auth example below to give you an MFA solution that fully supports TOTP or SMS MFA.

You may also find it useful to investigate [the local / manual test screens](https://github.com/invertase/react-native-firebase/blob/main/tests/local-tests/auth/auth-mfa-demonstrator.tsx) that we use to verify this functionality.

# Phone MFA

## iOS Setup

Make sure to follow [the official Identity Platform
documentation](https://cloud.google.com/identity-platform/docs/ios/mfa#enabling_multi-factor_authentication)
to enable multi-factor authentication for your project and verify your app.

## Enroll a new factor

Begin by obtaining a [`MultiFactorUser`](https://invertase.github.io/react-native-firebase/_react-native-firebase/auth/FirebaseAuthTypes/MultiFactorUser.html)
instance for the current user. This is the entry point for most multi-factor
operations:

```js
import {
  PhoneAuthProvider,
  PhoneMultiFactorGenerator,
  getAuth,
  multiFactor,
} from '@react-native-firebase/auth';

const multiFactorUser = await multiFactor(getAuth().currentUser);
```

Request the session identifier and use the phone number obtained from the user
to send a verification code:

```js
const session = await multiFactorUser.getSession();
const phoneOptions = {
  phoneNumber,
  session,
};

// Sends a text message to the user
const verificationId = await new PhoneAuthProvider(getAuth()).verifyPhoneNumber(phoneOptions);
```

Once the user has provided the verification code received by text message, you
can complete the process:

```js
const cred = PhoneAuthProvider.credential(verificationId, verificationCode);
const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(cred);
await multiFactorUser.enroll(multiFactorAssertion, 'Optional display name for the user');
```

You can inspect [`User#multiFactor`](https://invertase.github.io/react-native-firebase/_react-native-firebase/auth/FirebaseAuthTypes/User.html#multifactor) for
information about the user's enrolled factors.

## Sign-in flow using phone multi-factor

Ensure the account has already enrolled a second factor. Begin by calling the
default sign-in methods, for example email and password. If the account requires
a second factor to complete login, an exception will be raised:

```js
import {
  PhoneAuthProvider,
  PhoneMultiFactorGenerator,
  getAuth,
  signInWithEmailAndPassword,
  getMultiFactorResolver,
} from '@react-native-firebase/auth';

signInWithEmailAndPassword(getAuth(), email, password)
  .then(() => {
    // User has not enrolled a second factor
  })
  .catch(error => {
    const { code } = error;
    // Make sure to check if multi factor authentication is required
    if (code === 'auth/multi-factor-auth-required') {
      return;
    }

    // Other error
  });
```

Using the error object you can obtain a
[`MultiFactorResolver`](https://invertase.github.io/react-native-firebase/_react-native-firebase/auth/FirebaseAuthTypes/MultiFactorResolver.html) instance and
continue the flow:

```js
const resolver = getMultiFactorResolver(getAuth(), error);
```

The resolver object has all the required information to prompt the user for a
specific factor:

```js
if (resolver.hints.length > 1) {
  // Use resolver.hints to display a list of second factors to the user
}

if (resolver.hints[0].factorId === PhoneMultiFactorGenerator.FACTOR_ID) {
  // Continue with the sign-in flow
}
```

Using a multi-factor hint and the session information you can send a
verification code to the user:

```js
const hint = resolver.hints[0];

const verificationId = await new PhoneAuthProvider(getAuth()).verifyPhoneNumber({
  multiFactorHint: hint,
  session: resolver.session,
}); // Triggers message to user
```

Once the user has entered the verification code you can create a multi-factor
assertion and finish the flow:

```js
const credential = PhoneAuthProvider.credential(verificationId, verificationCode);

const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(credential);

resolver.resolveSignIn(multiFactorAssertion).then(userCredential => {
  // additionally onAuthStateChanged will be triggered as well
});
```

Upon successful sign-in, any
[`onAuthStateChanged`](/auth/usage#listening-to-authentication-state) listeners
will trigger with the new authentication state of the user.

To put the example together:

```js
import {
  PhoneAuthProvider,
  PhoneMultiFactorGenerator,
  getAuth,
  signInWithEmailAndPassword,
  getMultiFactorResolver,
} from '@react-native-firebase/auth';

signInWithEmailAndPassword(getAuth(), email, password)
  .then(() => {
    // User has not enrolled a second factor
  })
  .catch(error => {
    const { code } = error;
    // Make sure to check if multi factor authentication is required
    if (code === 'auth/multi-factor-auth-required') {
      const resolver = getMultiFactorResolver(getAuth(), error);

      if (resolver.hints.length > 1) {
        // Use resolver.hints to display a list of second factors to the user
      }

      if (resolver.hints[0].factorId === PhoneMultiFactorGenerator.FACTOR_ID) {
        const hint = resolver.hints[0];

        new PhoneAuthProvider(getAuth())
          .verifyPhoneNumber({ multiFactorHint: hint, session: resolver.session }) // triggers the message to the user
          .then(verificationId => setVerificationId(verificationId));

        // Request verificationCode from user

        const credential = PhoneAuthProvider.credential(verificationId, verificationCode);

        const multiFactorAssertion = PhoneMultiFactorGenerator.assertion(credential);

        resolver.resolveSignIn(multiFactorAssertion).then(userCredential => {
          // additionally onAuthStateChanged will be triggered as well
        });
      }
    }
  });
```

## Testing

You can define test phone numbers and corresponding verification codes. The
official[official
guide](https://cloud.google.com/identity-platform/docs/ios/mfa#enabling_multi-factor_authentication)
contains more information on setting this up.
```

### OpenID Connect Authentication

Source: https://rnfirebase.io/auth/oidc-auth

```mdx

React Native Firebase provides supports integrating with OpenID Connect providers. The authentication with these
different providers is left to you to implement due to the various implementations and flows possible.

Here we will demonstrate a minimal example of how you could do this using the package [react-native-app-auth](https://github.com/FormidableLabs/react-native-app-auth) to authenticate with the provider. Then after we have authenticated with the provider, we use the ID Token from the provider to authenticate with `react-native-firebase`. But you have to handle the flow to get the ID token and you should do things like logging the user out from the provider when they logout or revoke the token. Again this all depends on the provider, your flow and your use-case.

# Getting started

To get started with OpenID Connect authentication you need to do the following:

1. Setup or get the configuration from the provider you want to use
2. Add the provider in the firebase console
3. Authenticate in the app using `react-native-app-auth` and `react-native-firebase`

## 1. Setup or get the configuration from the provider you want to use

As stated before, this will vary a lot from provider to provider and your use-case. You need to find and look the documentation for the provider you want to use and follow that documentation to setup a working provider.
You can see examples of "Tested OpenID providers" from [react-native-app-auth here](https://github.com/FormidableLabs/react-native-app-auth#tested-openid-providers) and how you do this will depend on what provider you want to use. But you need to complete the setup or configuration of the provider you want to use before you continue here.

## 2. Add the provider in the Firebase console

Doing the steps below will allow you to add the provider to the Firebase project.
If the provider is not added there, you won't be able to use the `signInWithCredential` method, since Firebase will not be able to use the credential if the provider does not exist in the project.

1. Firebase console in the project you want to add OpenID Connect to
2. Authentication
3. Sign-in method
4. If you have added "Sign-in providers" already, click "Add new provider"
5. Under "Custom providers" choose "OpenID Connect"
6. Toggle on the Enabled at the top to the right of "Open ID Connect"
7. Fill out the details like: "Name", "Client ID", "Issuer (URL)" and "Client secret". These values have to correspond to the OpenID Connect provider you want to use.
8. Note down the Provider ID below name, if you type in "azure_test" in the name field. Notice how it says below the field: "Provider ID: oidc.azure_test" so this value will be prepended with "oidc." We will use this later when authenticating the user.

## 3. Authenticate in the app using "react-native-app-auth" and "react-native-firebase"

Before you use `react-native-app-auth` you have to complete the setup in their [docs](https://github.com/FormidableLabs/react-native-app-auth#getting-started).

The example below demonstrates how you could setup such a flow within your own application:

```jsx
import { OIDCAuthProvider, getAuth, signInWithCredential } from '@react-native-firebase/auth';
import { authorize } from 'react-native-app-auth';

// using react-native-app-auth to get oauth token from Azure AD
const config = {
  issuer: 'https://login.microsoftonline.com/XXX/v2.0',
  clientId: 'XXXX',
  redirectUrl: 'msauth.your.bundle.id://auth/',
  scopes: ['openid', 'profile', 'email', 'offline_access'],
  useNonce: false,
};

// Log in to get an authentication token
const authState = await authorize(config);

const credential = OIDCAuthProvider.credential(
  'azure_test', // this is the "Provider ID" value from the firebase console
  authState.idToken,
);

await signInWithCredential(getAuth(), credential);
```
```

### Phone Authentication

Source: https://rnfirebase.io/auth/phone-auth

```mdx

Phone authentication allows users to sign in to Firebase using their phone as the authenticator. An SMS message is sent
to the user via their phone number containing a unique code. Once the code has been authorized, the user is able to sign
in to Firebase.

Phone numbers that end users provide for authentication will be sent and stored by Google to improve our spam and abuse
prevention across Google services, including but not limited to Firebase. Developers should ensure they have appropriate
end-user consent prior to using the Firebase Authentication phone number sign-in service.

> Firebase Phone Auth is not supported in all countries. Please see their [FAQs](https://firebase.google.com/support/faq/#develop) for more information.

Ensure the "Phone" sign-in provider is enabled on the [Firebase Console](https://console.firebase.google.com/project/_/authentication/providers).

# iOS Setup

Ensure that all parts of step 1 and 2 from [the official firebase iOS phone auth docs](https://firebase.google.com/docs/auth/ios/phone-auth#enable-phone-number-sign-in-for-your-firebase-project) have been followed, noting in particular that you may need to re-download your firebase GoogleService-Info.plist file and for the reCAPTCHA flow to work you must make sure you have added your custom URL scheme to your project plist file.

Phone auth requires app verification, and the automatic app verification process uses data-only firebase cloud messages to the app. Data-only cloud messaging only works on real devices where the app has background refresh enabled. If background refresh disabled, or if using the Simulator, app verification uses the fallback reCAPTCHA flow allowing you to check if it is configured correctly.

For reliable automated testing, you may want to disable both automatic and fallback reCAPTCHA app verification for your app. To do this, [you may disable app verification in AuthSettings](https://reference.rnfirebase.io/_react-native-firebase/auth/FirebaseAuthTypes/AuthSettings.html#appVerificationDisabledForTesting) prior to calling any phone auth methods.

# Android Setup

Ensure that all parts of step 1 and 2 from [the official firebase Android phone auth docs](https://firebase.google.com/docs/auth/android/phone-auth#enable-phone-number-sign-in-for-your-firebase-project) have been followed.

To bypass Play Integrity for manual testing, you may [force reCAPTCHA to be used](https://reference.rnfirebase.io/_react-native-firebase/auth/FirebaseAuthTypes/AuthSettings.html#appVerificationDisabledForTesting) prior to calling [`verifyPhoneNumber`](https://reference.rnfirebase.io/_react-native-firebase/auth/verifyPhoneNumber.html).

# Expo Setup

To use phone auth in an expo app, add the `@react-native-firebase/auth` config plug-in to the [`plugins`](https://docs.expo.io/versions/latest/config/app/#plugins) section of your `app.json`. This is in addition to the `@react-native-firebase/app` plugin.

```json
{
  "expo": {
    "plugins": ["@react-native-firebase/app", "@react-native-firebase/auth"]
  }
}
```

The `@react-native-firebase/auth` config plugin is not required for all auth providers, but it is required to use phone auth. The plugin [will set up reCAPTCHA](https://firebase.google.com/docs/auth/ios/phone-auth#set-up-recaptcha-verification) verification for you on iOS.

The recommendation is to use a [custom development client](https://docs.expo.dev/develop/development-builds/introduction/#use-libraries-with-native-code-that-arent). For more info on using Expo with React Native Firebase, see our [Expo docs](/#installation-for-expo-projects).

# Sign-in

The module provides a `signInWithPhoneNumber` method which accepts a phone number. Firebase sends an SMS message to the
user with a code, which they must then confirm. The `signInWithPhoneNumber` method returns a confirmation method which accepts
a code. Based on whether the code is correct for the device, the method rejects or resolves.

The example below demonstrates how you could setup such a flow within your own application:

```jsx
import { useState, useEffect } from 'react';
import { Button, TextInput } from 'react-native';
import { getAuth, onAuthStateChanged, signInWithPhoneNumber } from '@react-native-firebase/auth';

function PhoneSignIn() {
  // If null, no SMS has been sent
  const [confirm, setConfirm] = useState(null);

  // verification code (OTP - One-Time-Passcode)
  const [code, setCode] = useState('');

  // Handle login
  function handleAuthStateChanged(user) {
    if (user) {
      // Some Android devices can automatically process the verification code (OTP) message, and the user would NOT need to enter the code.
      // Actually, if he/she tries to enter it, he/she will get an error message because the code was already used in the background.
      // In this function, make sure you hide the component(s) for entering the code and/or navigate away from this screen.
      // It is also recommended to display a message to the user informing him/her that he/she has successfully logged in.
    }
  }

  useEffect(() => {
    const subscriber = onAuthStateChanged(getAuth(), handleAuthStateChanged);
    return subscriber; // unsubscribe on unmount
  }, []);

  // Handle the button press
  async function handleSignInWithPhoneNumber(phoneNumber) {
    const confirmation = await signInWithPhoneNumber(getAuth(), phoneNumber);
    setConfirm(confirmation);
  }

  async function confirmCode() {
    try {
      await confirm.confirm(code);
    } catch (error) {
      console.log('Invalid code.');
    }
  }

  if (!confirm) {
    return (
      <Button
        title="Phone Number Sign In"
        onPress={() => handleSignInWithPhoneNumber('+1 650-555-3434')}
      />
    );
  }

  return (
    <>
      <TextInput value={code} onChangeText={text => setCode(text)} />
      <Button title="Confirm Code" onPress={() => confirmCode()} />
    </>
  );
}
```

Upon successful sign-in, any [`onAuthStateChanged`](/auth/usage#listening-to-authentication-state) listeners will trigger
with the new authentication state of the user.

# Testing

Firebase provides support for locally testing phone numbers. For local testing to work, you must have ensured your local
machine SHA1 debug key was added whilst creating your application on the Firebase Console. View the [Getting Started](/)
guide on how to set this up.

On the [Firebase Console](https://console.firebase.google.com/project/_/authentication/providers), select the "Phone" authentication provider and click on the
"Phone numbers for testing" dropdown.

Enter a new phone number (e.g. `+44 7444 555666`) and a test code (e.g. `123456`).

Once added, the number can be used with the `signInWithPhoneNumber` method, and entering the code specified will
cause a successful sign-in.

# MFA-like Account Creation

After successfully creating a user with an email and password (see Authentication/Usage/Email/Password sign-in), use the `verifyPhoneNumber` method to send a verification code to a user's phone number and if the user enters the correct code, link the phone number to the authenticated user's account. This creates a MFA-like authentication flow for account creation. However, to implement MFA with firebase, your app must call additional methods and use Google Cloud Identity Platform, which is a paid service, details available in this guide https://cloud.google.com/identity-platform/docs/web/mfa

```jsx
import React, { useState, useEffect } from 'react';
import { Button, TextInput, Text } from 'react-native';
import {
  PhoneAuthProvider,
  getAuth,
  onAuthStateChanged,
  createUserWithEmailAndPassword,
  verifyPhoneNumber,
} from '@react-native-firebase/auth';

export default function PhoneVerification() {
  // Set an initializing state whilst Firebase connects
  const [initializing, setInitializing] = useState(true);
  const [user, setUser] = useState();

  // If null, no SMS has been sent
  const [confirm, setConfirm] = useState(null);

  const [code, setCode] = useState('');

  // Handle user state changes
  function handleAuthStateChanged(user) {
    setUser(user);
    if (initializing) setInitializing(false);
  }

  useEffect(() => {
    const subscriber = onAuthStateChanged(getAuth(), handleAuthStateChanged);
    return subscriber; // unsubscribe on unmount
  }, []);

  // Handle create account button press
  async function createAccount() {
    try {
      await createUserWithEmailAndPassword(
        getAuth(),
        'jane.doe@example.com',
        'SuperSecretPassword!',
      );
      console.log('User account created & signed in!');
    } catch (error) {
      if (error.code === 'auth/email-already-in-use') {
        console.log('That email address is already in use!');
      }

      if (error.code === 'auth/invalid-email') {
        console.log('That email address is invalid!');
      }
      console.error(error);
    }
  }

  // Handle the verify phone button press
  async function handlePhoneNumberVerification(phoneNumber) {
    const confirmation = await verifyPhoneNumber(getAuth(), phoneNumber);
    setConfirm(confirmation);
  }

  // Handle confirm code button press
  async function confirmCode() {
    try {
      const credential = PhoneAuthProvider.credential(confirm.verificationId, code);
      let userData = await getAuth().currentUser.linkWithCredential(credential);
      setUser(userData.user);
    } catch (error) {
      if (error.code == 'auth/invalid-verification-code') {
        console.log('Invalid code.');
      } else {
        console.log('Account linking error');
      }
    }
  }

  if (initializing) return null;

  if (!user) {
    return <Button title="Login" onPress={() => createAccount()} />;
  } else if (!user.phoneNumber) {
    if (!confirm) {
      return (
        <Button
          title="Verify Phone Number"
          onPress={() =>
            handlePhoneNumberVerification('ENTER A VALID TESTING OR REAL PHONE NUMBER HERE')
          }
        />
      );
    }
    return (
      <>
        <TextInput value={code} onChangeText={text => setCode(text)} />
        <Button title="Confirm Code" onPress={() => confirmCode()} />
      </>
    );
  } else {
    return (
      <Text>
        Welcome! {user.phoneNumber} linked with {user.email}
      </Text>
    );
  }
}
```
```

### Social Authentication

Source: https://rnfirebase.io/auth/social-auth

```mdx

React Native Firebase provides support for integrating with different social platforms. The authentication with these
different platforms is left to the developer to implement due to the various implementations and flows possible using
their OAuth APIs.

# Social providers

## Apple

Starting April 2020, all existing applications using external 3rd party login services (such as Facebook, Twitter, Google etc)
must ensure that Apple Sign-In is also provided. To learn more about these new guidelines, view the [Apple announcement](https://developer.apple.com/news/?id=09122019b).
Apple Sign-In is not required for Android devices.

To integrate Apple Sign-In on your iOS applications, you need to install a 3rd party library to authenticate with Apple.
Once authentication is successful, a Firebase credential can be used to sign the user into Firebase with their Apple account.

To get started, you must first install the [`react-native-apple-authentication`](https://github.com/invertase/react-native-apple-authentication)
library. There are a number of [prerequisites](https://github.com/invertase/react-native-apple-authentication#prerequisites-to-using-this-library) to using the library, including
[setting up your Apple Developer account](https://github.com/invertase/react-native-apple-authentication/blob/main/docs/INITIAL_SETUP.md) to enable Apple Sign-In.

Ensure the "Apple" sign-in provider is enabled on the [Firebase Console](https://console.firebase.google.com/project/_/authentication/providers).

Once setup, we can trigger an initial request to allow the user to sign in with their Apple account, using a pre-rendered
button the `react-native-apple-authentication` library provides:

```jsx
import React from 'react';
import { AppleButton } from '@invertase/react-native-apple-authentication';

function AppleSignIn() {
  return (
    <AppleButton
      buttonStyle={AppleButton.Style.WHITE}
      buttonType={AppleButton.Type.SIGN_IN}
      style={{
        width: 160,
        height: 45,
      }}
      onPress={() => onAppleButtonPress().then(() => console.log('Apple sign-in complete!'))}
    />
  );
}
```

When the user presses the pre-rendered button, we can trigger the initial sign-in request using the `performRequest` method,
passing in the scope required for our application:

```js
import { AppleAuthProvider, getAuth, signInWithCredential } from '@react-native-firebase/auth';
import { appleAuth } from '@invertase/react-native-apple-authentication';

async function onAppleButtonPress() {
  // Start the sign-in request
  const appleAuthRequestResponse = await appleAuth.performRequest({
    requestedOperation: appleAuth.Operation.LOGIN,
    // As per the FAQ of react-native-apple-authentication, the name should come first in the following array.
    // See: https://github.com/invertase/react-native-apple-authentication#faqs
    requestedScopes: [appleAuth.Scope.FULL_NAME, appleAuth.Scope.EMAIL],
  });

  // Ensure Apple returned a user identityToken
  if (!appleAuthRequestResponse.identityToken) {
    throw new Error('Apple Sign-In failed - no identify token returned');
  }

  // Create a Firebase credential from the response
  const { identityToken, nonce } = appleAuthRequestResponse;
  const appleCredential = AppleAuthProvider.credential(identityToken, nonce);

  // Sign the user in with the credential
  return signInWithCredential(getAuth(), appleCredential);
}
```

Upon successful sign-in, any [`onAuthStateChanged`](/auth/usage#listening-to-authentication-state) listeners will trigger
with the new authentication state of the user.

Apple also requires that the app revoke the `Sign in with Apple` token when the user chooses to delete their account. This can be accomplished with the `revokeToken` API.

```js
import { getAuth, revokeToken } from '@react-native-firebase/auth';
import { appleAuth } from '@invertase/react-native-apple-authentication';

async function revokeSignInWithAppleToken() {
  // Get an authorizationCode from Apple
  const { authorizationCode } = await appleAuth.performRequest({
    requestedOperation: appleAuth.Operation.REFRESH,
  });

  // Ensure Apple returned an authorizationCode
  if (!authorizationCode) {
    throw new Error('Apple Revocation failed - no authorizationCode returned');
  }

  // Revoke the token
  return revokeToken(getAuth(), authorizationCode);
}
```

## Facebook

There is a [community-supported React Native library](https://github.com/thebergamo/react-native-fbsdk-next) which wraps around
the native Facebook SDKs to enable Facebook sign-in.

Before getting started, ensure you have installed the library, [configured your Android & iOS applications](https://developers.facebook.com/docs/android/getting-started/) and
setup your [Facebook Developer Account](https://github.com/thebergamo/react-native-fbsdk-next#3-configure-projects)
to enable Facebook Login.

Ensure the "Facebook" sign-in provider is enabled on the [Firebase Console](https://console.firebase.google.com/project/_/authentication/providers).

Once setup, we can trigger the login flow with Facebook by calling the `logInWithPermissions` method on the `LoginManager`
class:

```jsx
import React from 'react';
import { Button } from 'react-native';

function FacebookSignIn() {
  return (
    <Button
      title="Facebook Sign-In"
      onPress={() => onFacebookButtonPress().then(() => console.log('Signed in with Facebook!'))}
    />
  );
}
```

The `onFacebookButtonPress` can then be implemented as follows:

```js
import { FacebookAuthProvider, getAuth, signInWithCredential } from '@react-native-firebase/auth';
import { LoginManager, AccessToken } from 'react-native-fbsdk-next';

async function onFacebookButtonPress() {
  // Attempt login with permissions
  const result = await LoginManager.logInWithPermissions(['public_profile', 'email']);

  if (result.isCancelled) {
    throw 'User cancelled the login process';
  }

  // Once signed in, get the users AccessToken
  const data = await AccessToken.getCurrentAccessToken();

  if (!data) {
    throw 'Something went wrong obtaining access token';
  }

  // Create a Firebase credential with the AccessToken
  const facebookCredential = FacebookAuthProvider.credential(data.accessToken);

  // Sign-in the user with the credential
  return signInWithCredential(getAuth(), facebookCredential);
}
```

### Facebook Limited Login (iOS only)

To use Facebook Limited Login instead of "classic" Facebook Login, the `onFacebookButtonPress` can then be implemented as follows:

```js
import { FacebookAuthProvider, getAuth, signInWithCredential } from '@react-native-firebase/auth';
import { LoginManager, AuthenticationToken } from 'react-native-fbsdk-next';
import { sha256 } from 'react-native-sha256';

async function onFacebookButtonPress() {
  // Create a nonce and the corresponding
  // sha256 hash of the nonce
  const nonce = '123456';
  const nonceSha256 = await sha256(nonce);
  // Attempt login with permissions and limited login
  const result = await LoginManager.logInWithPermissions(
    ['public_profile', 'email'],
    'limited',
    nonceSha256,
  );

  if (result.isCancelled) {
    throw 'User cancelled the login process';
  }

  // Once signed in, get the users AuthenticationToken
  const data = await AuthenticationToken.getAuthenticationTokenIOS();

  if (!data) {
    throw 'Something went wrong obtaining authentication token';
  }

  // Create a Firebase credential with the AuthenticationToken
  // and the nonce (Firebase will validates the hash against the nonce)
  const facebookCredential = FacebookAuthProvider.credential(data.authenticationToken, nonce);

  // Sign-in the user with the credential
  return signInWithCredential(getAuth(), facebookCredential);
}
```

Upon successful sign-in, any [`onAuthStateChanged`](/auth/usage#listening-to-authentication-state) listeners will trigger
with the new authentication state of the user.

## Google

Firebase does not ship a Google sign-in UI. You install a community library to obtain a Google **ID token**, then pass it to `GoogleAuthProvider.credential()` and `signInWithCredential()`.

Ensure the "Google" sign-in provider is enabled on the [Firebase Console](https://console.firebase.google.com/project/_/authentication/providers).

On **Android**, Google recommends [Credential Manager](https://developer.android.com/identity/sign-in/credential-manager-siwg-implementation) for Sign in with Google. Two maintained React Native libraries can provide that flow:

| Library                                                                                                    | Credential Manager (Android)                                                                                                          | Cost                                                     | Notes                                                                                                               |
| ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| [`react-native-nitro-google-signin`](https://www.npmjs.com/package/react-native-nitro-google-signin)       | Yes                                                                                                                                   | Free (MIT)                                               | Universal / One Tap-style API via [Nitro Modules](https://nitro.margelo.com); requires `react-native-nitro-modules` |
| [`@react-native-google-signin/google-signin`](https://github.com/react-native-google-signin/google-signin) | Yes with [Universal Sign-In](https://react-native-google-signin.github.io/docs/install); **no** on the free public `GoogleSignin` API | Paid for Credential Manager; free for legacy Android SDK | Universal Sign-In uses `GoogleOneTapSignIn`; the free public build is documented in the section below               |

The sections below show Firebase integration for each approach. For native setup (OAuth clients, SHA-1, Expo config plugins), follow that library's documentation.

### react-native-nitro-google-signin

[`react-native-nitro-google-signin`](https://www.npmjs.com/package/react-native-nitro-google-signin) is a free, MIT-licensed option that uses **Credential Manager** on Android and the Google Sign-In SDK on iOS. It is a good fit when you want modern Android sign-in without a paid Universal Sign-In license from `@react-native-google-signin/google-signin`.

Install the package and its peer dependency:

```bash
yarn add react-native-nitro-google-signin react-native-nitro-modules
```

Then follow the [installation](https://react-native-nitro-google-sign-in.github.io/docs/getting-started/installation) and [Google Cloud setup](https://react-native-nitro-google-sign-in.github.io/docs/setup/google-cloud) guides. For Expo, use a [development build](https://docs.expo.dev/develop/development-builds/introduction/) and the [Expo config plugin](https://react-native-nitro-google-sign-in.github.io/docs/setup/expo) — it does not run in Expo Go.

#### Using Google Sign-In with Firebase

Configure once (with Firebase config files present, `webClientId: 'autoDetect'` reads the Web client ID from `google-services.json` / `GoogleService-Info.plist`):

```js
import { GoogleOneTapSignIn } from 'react-native-nitro-google-signin';

GoogleOneTapSignIn.configure({ webClientId: 'autoDetect' });
```

Trigger sign-in from your UI, then exchange the ID token with Firebase:

```jsx
import { Button } from 'react-native';

function GoogleSignIn() {
  return (
    <Button
      title="Google Sign-In"
      onPress={() => onGoogleButtonPress().then(() => console.log('Signed in with Google!'))}
    />
  );
}
```

```js
import { GoogleAuthProvider, getAuth, signInWithCredential } from '@react-native-firebase/auth';
import {
  GoogleOneTapSignIn,
  isNoSavedCredentialFoundResponse,
  isSuccessResponse,
} from 'react-native-nitro-google-signin';

async function onGoogleButtonPress() {
  await GoogleOneTapSignIn.checkPlayServices();

  let response = await GoogleOneTapSignIn.signIn();

  if (isNoSavedCredentialFoundResponse(response)) {
    response = await GoogleOneTapSignIn.createAccount();
  }
  if (isNoSavedCredentialFoundResponse(response)) {
    response = await GoogleOneTapSignIn.presentExplicitSignIn();
  }

  if (!isSuccessResponse(response)) {
    throw new Error('Google Sign-In was cancelled or failed');
  }

  const idToken = response.data?.idToken;
  if (!idToken) {
    throw new Error('No ID token found');
  }

  const googleCredential = GoogleAuthProvider.credential(idToken);
  return signInWithCredential(getAuth(), googleCredential);
}
```

Upon successful sign-in, any [`onAuthStateChanged`](/auth/usage#listening-to-authentication-state) listeners will trigger with the new authentication state.

If you test on an Android emulator, use an image with **Google APIs** or **Google Play** system images.

Full API details: [react-native-nitro-google-signin documentation](https://react-native-nitro-google-sign-in.github.io/).

### @react-native-google-signin/google-signin

The [`@react-native-google-signin/google-signin`](https://github.com/react-native-google-signin/google-signin) package is widely used and offers two tiers:

- **Universal Sign-In** ([paid](https://universal-sign-in.com/#pricing)): Cross-platform One Tap-style API (`GoogleOneTapSignIn`) with **Credential Manager** on Android. Licensed builds are installed from the package maintainer's registry — see [their installation guide](https://react-native-google-signin.github.io/docs/install).
- **Public (free) version**: `GoogleSignin` API for Android and iOS using the **legacy** Android Google Sign-In SDK. Google has deprecated that stack; it does not use Credential Manager. The examples below use this free API.

#### Configure an Expo project

For Expo projects, follow [the setup instructions for Expo](https://react-native-google-signin.github.io/docs/category/setting-up) from `@react-native-google-signin/google-signin`.

#### Configure a React-Native (non-Expo) project

For bare React-Native projects, most configuration is already setup when using Google Sign-In with React-Native-Firebase's configuration, however you need to ensure your machines SHA1 key has been configured for use with Android. You can see how to generate the key on the [Getting Started](/) documentation.

#### Using Google Sign-In

Before triggering a sign-in request, you must initialize the Google SDK with any required scopes and the
`webClientId`, which can be found in the `android/app/google-services.json` file as the `client/oauth_client/client_id` property (the id ends with `.apps.googleusercontent.com`). Make sure to pick the `client_id` with `client_type: 3`

```js
import { GoogleSignin } from '@react-native-google-signin/google-signin';

GoogleSignin.configure({
  webClientId: '',
});
```

Once initialized, setup your application to trigger a sign-in request with Google using the `signIn` method.

```jsx
import { Button } from 'react-native';

function GoogleSignIn() {
  return (
    <Button
      title="Google Sign-In"
      onPress={() => onGoogleButtonPress().then(() => console.log('Signed in with Google!'))}
    />
  );
}
```

The `onGoogleButtonPress` can then be implemented as follows:

```js
import { GoogleAuthProvider, getAuth, signInWithCredential } from '@react-native-firebase/auth';
import { GoogleSignin } from '@react-native-google-signin/google-signin';

async function onGoogleButtonPress() {
  // Check if your device supports Google Play
  await GoogleSignin.hasPlayServices({ showPlayServicesUpdateDialog: true });
  // Get the users ID token
  const signInResult = await GoogleSignin.signIn();

  // Try the new style of google-sign in result, from v13+ of that module
  idToken = signInResult.data?.idToken;
  if (!idToken) {
    // if you are using older versions of google-signin, try old style result
    idToken = signInResult.idToken;
  }
  if (!idToken) {
    throw new Error('No ID token found');
  }

  // Create a Google credential with the token
  const googleCredential = GoogleAuthProvider.credential(signInResult.data.idToken);

  // Sign-in the user with the credential
  return signInWithCredential(getAuth(), googleCredential);
}
```

Upon successful sign-in, any [`onAuthStateChanged`](/auth/usage#listening-to-authentication-state) listeners will trigger
with the new authentication state of the user.

If you are testing this feature on an android emulator ensure that the emulate is either the Google APIs or Google Play flavor.

> If you need Credential Manager on Android without a paid license, use [`react-native-nitro-google-signin`](/auth/social-auth#react-native-nitro-google-signin) instead of upgrading to Universal Sign-In.

## Microsoft

Per the [documentation](https://firebase.google.com/docs/auth/android/microsoft-oauth#expandable-1), we cannot handle the Sign-In flow manually, by getting the access token from a library such as `react-native-app-auth`, and then calling `signInWithCredential`.
Instead, we must use the native's Sign-In flow from the Firebase SDK.

To get started, please follow the prerequisites and setup instructions from the documentation: [Android](https://firebase.google.com/docs/auth/android/microsoft-oauth#before_you_begin), [iOS](https://firebase.google.com/docs/auth/ios/microsoft-oauth#before_you_begin).

Additionally, for iOS, please follow step 1 of the "Handle sign-in flow" [section](https://firebase.google.com/docs/auth/ios/microsoft-oauth#handle_the_sign-in_flow_with_the_firebase_sdk), which is to add the custom URL scheme to your Xcode project

Once completed, setup your application to trigger a sign-in request with Microsoft using either of the `signInWithPopup` or `signInWithRedirect` methods. The underlying implementation is the same and will not operate exactly as the firebase-js-sdk web-based implementations do, but will provide drop-in compatibility for a web implementation if your project has one.

```jsx
import React from 'react';
import { Button } from 'react-native';

function MicrosoftSignIn() {
  return (
    <Button
      title="Microsoft Sign-In"
      onPress={() => onMicrosoftButtonPress().then(() => console.log('Signed in with Microsoft!'))}
    />
  );
}
```

`onMicrosoftButtonPress` can be implemented as the following:

```js
import { OAuthProvider, getAuth, signInWithRedirect } from '@react-native-firebase/auth';

const onMicrosoftButtonPress = async () => {
  // Generate the provider object
  const provider = new OAuthProvider('microsoft.com');
  // Optionally add scopes
  provider.addScope('offline_access');
  // Optionally add custom parameters
  provider.setCustomParameters({
    prompt: 'consent',
    // Optional "tenant" parameter for optional use of Azure AD tenant.
    // e.g., specific ID - 9aaa9999-9999-999a-a9aa-9999aa9aa99a or domain - example.com
    // defaults to "common" for tenant-independent tokens.
    tenant: 'tenant_name_or_id',
  });

  // Sign-in the user with the provider
  return signInWithRedirect(getAuth(), provider);
};
```

Additionally, the similar `linkWithRedirect` and `linkWithPopup` methods may be used in the same way to link an existing user account with the Microsoft account after it is authenticated.

Upon successful sign-in, any [`onAuthStateChanged`](/auth/usage#listening-to-authentication-state) listeners will trigger
with the new authentication state of the user.

## Twitter

Using the external [`@react-native-twitter-signin/twitter-signin`](https://github.com/react-native-twitter-signin/twitter-signin) library,
we can sign-in the user with Twitter and generate a credential which can be used to sign-in with Firebase.

To get started, install the library and ensure you have completed setup, following the required [prerequisites](https://github.com/react-native-twitter-signin/twitter-signin#prerequisites) list.

Ensure the "Twitter" sign-in provider is enabled on the [Firebase Console](https://console.firebase.google.com/project/_/authentication/providers).

Before triggering a sign-in request, you must initialize the Twitter SDK using your accounts consumer key & secret:

```js
import { NativeModules } from 'react-native';
const { RNTwitterSignIn } = NativeModules;

RNTwitterSignIn.init('TWITTER_CONSUMER_KEY', 'TWITTER_CONSUMER_SECRET').then(() =>
  console.log('Twitter SDK initialized'),
);
```

Once initialized, setup your application to trigger a sign-in request with Twitter using the `login` method.

```jsx
import React from 'react';
import { Button } from 'react-native';

function TwitterSignIn() {
  return (
    <Button
      title="Twitter Sign-In"
      onPress={() => onTwitterButtonPress().then(() => console.log('Signed in with Twitter!'))}
    />
  );
}
```

The `onTwitterButtonPress` can then be implemented as follows:

```js
import { TwitterAuthProvider, getAuth, signInWithCredential } from '@react-native-firebase/auth';
import { NativeModules } from 'react-native';
const { RNTwitterSignIn } = NativeModules;

async function onTwitterButtonPress() {
  // Perform the login request
  const { authToken, authTokenSecret } = await RNTwitterSignIn.logIn();

  // Create a Twitter credential with the tokens
  const twitterCredential = TwitterAuthProvider.credential(authToken, authTokenSecret);

  // Sign-in the user with the credential
  return signInWithCredential(getAuth(), twitterCredential);
}
```

Upon successful sign-in, any [`onAuthStateChanged`](/auth/usage#listening-to-authentication-state) listeners will trigger
with the new authentication state of the user.

## Link Multiple Auth Providers to a Firebase Account

[From the official documentation:](https://firebase.google.com/docs/auth/web/google-signin#expandable-1)

> If you enabled the **One account per email address** setting in the Firebase console, when a user tries to sign in a to a provider (such as Google) with an email that already exists for another Firebase user's provider (such as Facebook), the error `auth/account-exists-with-different-credential` is thrown along with an `AuthCredential` object (Google ID token). To complete the sign in to the intended provider, the user has to sign first to the existing provider (Facebook) and then link to the former `AuthCredential` (Google ID token).

To provide users with an additional login method, you can link their social media account (or an email & password) with their Firebase account.
This is possible for any social provider that uses `signInWithCredential()`.
To achieve this, you should replace the sign-in method in any of the supported social sign-in code snippets with `linkWithCredential()` on the signed-in user. Ensure `getAuth().currentUser` is not null before linking.

This code demonstrates linking a Google provider to an account that is already signed in using Firebase authentication.

With [`react-native-nitro-google-signin`](https://www.npmjs.com/package/react-native-nitro-google-signin):

```js
import { GoogleAuthProvider, getAuth } from '@react-native-firebase/auth';
import {
  GoogleOneTapSignIn,
  isNoSavedCredentialFoundResponse,
  isSuccessResponse,
} from 'react-native-nitro-google-signin';

async function onGoogleLinkButtonPress() {
  await GoogleOneTapSignIn.checkPlayServices();

  let response = await GoogleOneTapSignIn.signIn();
  if (isNoSavedCredentialFoundResponse(response)) {
    response = await GoogleOneTapSignIn.presentExplicitSignIn();
  }
  if (!isSuccessResponse(response) || !response.data?.idToken) {
    throw new Error('Google Sign-In failed');
  }

  const user = getAuth().currentUser;
  if (!user) {
    throw new Error('No user is signed in');
  }

  const googleCredential = GoogleAuthProvider.credential(response.data.idToken);
  await user.linkWithCredential(googleCredential);
}
```

With the free `@react-native-google-signin/google-signin` (`GoogleSignin`) API:

```js
import { GoogleAuthProvider, getAuth } from '@react-native-firebase/auth';
import { GoogleSignin } from '@react-native-google-signin/google-signin';

async function onGoogleLinkButtonPress() {
  await GoogleSignin.hasPlayServices({ showPlayServicesUpdateDialog: true });
  const signInResult = await GoogleSignin.signIn();

  const idToken = signInResult.data?.idToken ?? signInResult.idToken;
  if (!idToken) {
    throw new Error('No ID token found');
  }

  const user = getAuth().currentUser;
  if (!user) {
    throw new Error('No user is signed in');
  }

  const googleCredential = GoogleAuthProvider.credential(idToken);
  await user.linkWithCredential(googleCredential);
}
```
```

### Authentication

Source: https://rnfirebase.io/auth/usage

```mdx

# Installation

This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app"
module, view the [Getting Started](/) documentation.

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the authentication module
yarn add @react-native-firebase/auth

# If you're developing your app using iOS, run this command
cd ios/ && pod install
```

If you're using an older version of React Native without autolinking support, or wish to integrate into an existing project,
you can follow the manual installation steps for [iOS](/auth/usage/installation/ios) and [Android](/auth/usage/installation/android).

# Platform support and New Architecture

|                      |                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| **Platforms**        | Android, iOS (native Firebase SDK)                                                             |
| **New Architecture** | **Required** from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement). |

**Platform notes:** Several firebase-js-sdk web helpers throw on React Native (`setPersistence`, `getRedirectResult`, `revokeAccessToken`, `useDeviceLanguage`, `linkWithPhoneNumber`, `reauthenticateWithPhoneNumber`). `useUserAccessGroup` is **iOS only**. See [Migrating to v26 — Platform behavior differences](/migrating-to-v26#platform-behavior-differences).

# What does it do

Firebase Authentication provides backend services & easy-to-use SDKs to authenticate users to your app. It supports
authentication using passwords, phone numbers, popular federated identity providers like Google, Facebook and Twitter, and more.

<YouTube id="8sGY55yxicA" />

Firebase Authentication integrates tightly with other Firebase services, and it leverages industry standards like OAuth
2.0 and OpenID Connect, so it can be easily integrated with your custom backend.

# Usage

## Listening to authentication state

In most scenarios using Authentication, you will want to know whether your users are currently signed-in or signed-out
of your application. The module provides a method called `onAuthStateChanged` which allows you to subscribe to the users
current authentication state, and receive an event whenever that state changes.

It is important to remember the `onAuthStateChanged` listener is asynchronous and will trigger an initial state once
a connection with Firebase has been established. Therefore it is important to setup an "initializing" state which blocks
render of our main application whilst the connection is established:

```jsx
import React, { useState, useEffect } from 'react';
import { View, Text } from 'react-native';
import { getAuth, onAuthStateChanged } from '@react-native-firebase/auth';

function App() {
  // Set an initializing state whilst Firebase connects
  const [initializing, setInitializing] = useState(true);
  const [user, setUser] = useState();

  // Handle user state changes
  function handleAuthStateChanged(user) {
    setUser(user);
    if (initializing) setInitializing(false);
  }

  useEffect(() => {
    const subscriber = onAuthStateChanged(getAuth(), handleAuthStateChanged);
    return subscriber; // unsubscribe on unmount
  }, []);

  if (initializing) return null;

  if (!user) {
    return (
      <View>
        <Text>Login</Text>
      </View>
    );
  }

  return (
    <View>
      <Text>Welcome {user.email}</Text>
    </View>
  );
}
```

If the `user` returned within the handler is `null` we assume the user is currently signed-out, otherwise they are
signed-in and a [`User`](https://invertase.github.io/react-native-firebase/_react-native-firebase/auth/FirebaseAuthTypes/User.html) interface is returned.

The `onAuthStateChanged` method also returns an unsubscriber function which allows us to stop listening for events whenever
the hook is no longer in use.

## Persisting authentication state

On web based applications, the Firebase Web SDK takes advantage of features such as cookies and local storage to persist
the users authenticated state across sessions. The native Firebase SDKs also provide this functionality using device native SDKs,
ensuring that a users previous authentication state between app sessions is persisted.

The user is able to clear their state by deleting the apps data/cache from the device settings.

## Anonymous sign-in

Some applications don't require authentication, which make it tricky to identify what users are doing throughout your app.
If connecting with external APIs, it is also useful to add an extra layer of security by ensuring the users request is
from the app. This can be achieved with the `signInAnonymously` method, which creates a new anonymous user which is persisted,
allowing you to integrate with other services such as Analytics by providing a user ID.

Ensure the "Anonymous" sign-in provider is enabled on the [Firebase Console](https://console.firebase.google.com/project/_/authentication/providers).

```js
import { getAuth, signInAnonymously } from '@react-native-firebase/auth';

signInAnonymously(getAuth())
  .then(() => {
    console.log('User signed in anonymously');
  })
  .catch(error => {
    if (error.code === 'auth/operation-not-allowed') {
      console.log('Enable anonymous in your firebase console.');
    }

    console.error(error);
  });
```

Once successfully signed in, any [`onAuthStateChanged`](/auth/usage#listening-to-authentication-state) listeners will trigger an event
with the [`User`](https://invertase.github.io/react-native-firebase/_react-native-firebase/auth/FirebaseAuthTypes/User.html) details.

In case any errors occur, the module provides support for identifying what specifically went wrong by attaching a code
to the error. For a full list of error codes available, view the [Firebase documentation](https://firebase.google.com/docs/reference/js/auth.md#autherrorcodes).

## Email/Password sign-in

Email/password sign in is a common method for user sign in on applications. This requires the user to provide an email
address and secure password. Users can both register and sign in using a method called `createUserWithEmailAndPassword`
or sign in to an existing account with `signInWithEmailAndPassword`.

Ensure the "Email/Password" sign-in provider is enabled on the [Firebase Console](https://console.firebase.google.com/project/_/authentication/providers).

The `createUserWithEmailAndPassword` performs two operations; first creating the user if they do not already exist, and
then signing them in.

```js
import { getAuth, createUserWithEmailAndPassword } from '@react-native-firebase/auth';

createUserWithEmailAndPassword(getAuth(), 'jane.doe@example.com', 'SuperSecretPassword!')
  .then(() => {
    console.log('User account created & signed in!');
  })
  .catch(error => {
    if (error.code === 'auth/email-already-in-use') {
      console.log('That email address is already in use!');
    }

    if (error.code === 'auth/invalid-email') {
      console.log('That email address is invalid!');
    }

    console.error(error);
  });
```

Once successfully created and/or signed in, any [`onAuthStateChanged`](/auth/usage#listening-to-authentication-state) listeners will trigger an event
with the [`User`](https://invertase.github.io/react-native-firebase/_react-native-firebase/auth/FirebaseAuthTypes/User.html) details.

In case any errors occur, the module provides support for identifying what specifically went wrong by attaching a code
to the error. For a full list of error codes available, view the [Firebase documentation](https://firebase.google.com/docs/reference/js/auth.md#autherrorcodes).

## Authenticate with backend server

The user's token should be used for authentication with your backend systems. The token is fetched with the [getIdToken](https://reference.rnfirebase.io/_react-native-firebase/auth/FirebaseAuthTypes/User.html#getIdToken) method. As mentioned in the [Firebase documentation](https://firebase.google.com/docs/auth/web/manage-users#get_a_users_profile), the uid should not be used for authentication.

## Signing out

If you'd like to sign the user out of their current authentication state, call the `signOut` method:

```js
import { getAuth, signOut } from '@react-native-firebase/auth';

signOut(getAuth()).then(() => console.log('User signed out!'));
```

Once successfully signed out, any [`onAuthStateChanged`](/auth/usage#listening-to-authentication-state) listeners will trigger an event
with the `user` parameter being a `null` value.

Additionally, calling `revokeAccess()` on your Google sign-in library forgets the user on the device (for example `GoogleOneTapSignIn.revokeAccess()` from [`react-native-nitro-google-signin`](https://www.npmjs.com/package/react-native-nitro-google-signin), or `GoogleSignin.revokeAccess()` from `@react-native-google-signin/google-signin`). The next sign-in will show the account picker again. Without it, the last account may be reused automatically.

## Other sign-in methods

Firebase also supports authenticating with external provides. To learn more, view the documentation for your authentication
method:

- [Apple Sign-In](/auth/social-auth#apple).
- [Facebook Sign-In](/auth/social-auth#facebook).
- [Twitter Sign-In](/auth/social-auth#twitter).
- [Google Sign-In](/auth/social-auth#google).
- [Microsoft Sign-In](/auth/social-auth#microsoft).
- [Phone Number Sign-In](/auth/phone-auth).
- [Email Link Sign-In](/auth/email-link-auth) (passwordless App Links / Universal Links).
```

### Crashlytics - Android Setup

Source: https://rnfirebase.io/crashlytics/android-setup

```mdx

> If you're migrating from Fabric, make sure you remove the `fabric.properties` file from your Android project. If you do not do this you will not receive crash reports on the Firebase console.

> If you're using Expo, make sure to add the `@react-native-firebase/crashlytics` config plugin to your `app.json` or `app.config.js`. It handles the below installation steps for you. For instructions on how to do that, view the [Expo](/#expo) installation section.

# Adding Firebase Crashlytics Gradle Tools

These steps are required, if you do not add these your app will most likely crash at startup with the following Error:

"The Crashlytics build ID is missing. This occurs when Crashlytics tooling is absent from your app's build configuration.
Please review Crashlytics onboarding instructions and ensure you have a valid Crashlytics account."\_

## 1. Add the Google repository (if it's not there already)

Add the following line to the `android/build.gradle` file :

```groovy
// ..
buildscript {
  // ..
  repositories {
    // ..
    google()
  }
  // ..
}
```

## 2. Add the Firebase Crashlytics Plugin dependency

Add the following dependency to the `android/build.gradle` file:

```groovy
// ..
buildscript {
  // ..
  dependencies {
    // ..
    classpath 'com.google.firebase:firebase-crashlytics-gradle:3.0.8'
  }
  // ..
}
```

## 3. Apply the Firebase Crashlytics Plugin to your app

Apply the `com.google.firebase.crashlytics` plugin by adding the following to the top of your `android/app/build.gradle` file:

```
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services' // apply after this line
apply plugin: 'com.google.firebase.crashlytics'
// ..
```

## 4. (Optional) Enable Crashlytics NDK reporting

Crashlytics NDK reporting allows you to capture Native Development Kit crashes, e.g. in React Native this will capture
crashes originating from the Yoga layout engine.

Add the `firebaseCrashlytics` block line to the `android/app/build.gradle` file:

```groovy
android {
    // ...

    buildTypes {
        release {
            /* Add the firebaseCrashlytics extension (by default,
            * it's disabled to improve build speeds) and set
            * nativeSymbolUploadEnabled to true along with a pointer to native libs. */

            firebaseCrashlytics {
                nativeSymbolUploadEnabled true
                unstrippedNativeLibsDir 'build/intermediates/merged_native_libs/release/out/lib'
            }
            // ...
        }
    }
}
```

## 5. Rebuild the project

Once the above steps have been completed, rebuild your Android project:

```bash
npx react-native run-android
```
```

### Crashlytics in the Firebase Console

Source: https://rnfirebase.io/crashlytics/crash-reports

```mdx

# Overview

Once you have Crashlytics up and running in your app, you can navigate to Crashlytics in your Firebase Console underneath
'Quality' and start reviewing the reports as they come in. If this page still tells you to setup, build or run your app
then you have not correctly setup Crashlytics in your app (see [Usage](/crashlytics/usage)).

> Keep in mind when testing out Crashlytics that Crashlytics is [disabled by default in debug mode](/crashlytics/usage#enable-debug-crash-logs). You may even find a more in-depth guide useful if you are really struggling with testing your crash report integration - we have [an in-depth article about configuring and testing Crashlytics to help you.](https://invertase.io/blog/react-native-firebase-crashlytics-configuration)

Upon running the first two examples under [usage](/crashlytics/usage), you will be shown a display similar to following image.
![](https://i.imgur.com/YIQ88ZF.png)
In this example the [Crash Attributes](/crashlytics/usage#crash-attributes) example was ran four times and the [Error Reports](/crashlytics/usage#error-reports) example three times, in addition to several miscellaneous exceptions that have occurred during the write-up.

> If you are certain that your app has produced error reports, but none are visible, try restarting your app fully. Crashlytics only uploads reports upon launching the app.

## Issues

Under issues, Firebase has gathered all the reports from your app and organized them into separate issues, where each
issue is a unique crash or stack trace in your app. One of the issues visible in the example shown originates from `CrashTest.java`,
which is the Android module responsible for testing Crashlytics through the `crash` method, throwing an uncaught exception
to crash the app. Using this method on the same platform will always add reports to the same issue.

# Managing issues

By clicking on a specific issue, you can view its statistics and all the associated reports in more detail. In this example
we're viewing the `CrashTest.java` issue.

![Crash Example](https://i.imgur.com/XYBNuJx.png)

Here you're shown the breakdown of this issue by date, device and operating system. On the bottom you can browse specific
reports and view their specific contents. We have selected the logs part of the report, showing the two custom log messages
that our example generated. Note that there is a third `Crash Test` log automatically generated by Crashlytics upon using the
`crash` method. By navigating to data you can view information about the platform where the report was generated and information
about the associated user. Under keys you can see the custom attributes that we have attached to the report.

## Closing issues

Note that in the top right there is a button that says 'Close'. After having addressed an issue you can close it, allowing
you to filter it out in the overview by selecting 'Open' under 'Issue state', when clicking on the 'Filter issues' button.
This is visible in the first example, where we're displaying only 3 relevant non-fatal events out of the 12 that occurred.
When the same issue re-occurs, it will automatically open again. By clicking the arrow next to the button we can mute the
issue, preventing this from happening.

# Android ANR Collection Support

The Firebase Team enabled the ability to collect
Application Not Responding (ANR) issues that occur when the UI thread of an Android app is blocked for too long,
for more information on ANR see the
[android developer documentation](https://developer.android.com/topic/performance/vitals/anr).

The support for ANR collection is added in the Android Crashlytics version
[18.2.4](https://firebase.google.com/support/release-notes/android#crashlytics_v18-2-4)
so that your react native application can collect ANR, make sure you are using at least version 13.0.1 of this library
```

### Crashlytics

Source: https://rnfirebase.io/crashlytics/usage

```mdx

# Installation

This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app" module, view the
[Getting Started](/) documentation.

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the Crashlytics module
yarn add @react-native-firebase/crashlytics

# If you're developing your app using iOS, run this command
cd ios/ && pod install
```

Once installed, you must complete the following additional setup steps for Android:

- [Android Additional Setup](/crashlytics/android-setup).

> If you're using Expo, make sure to add the `@react-native-firebase/crashlytics` config plugin to your `app.json` or `app.config.js`. It handles the Android installation steps for you. For instructions on how to do that, view the [Expo](/#expo) installation section.

If you're using an older version of React Native without autolinking support, or wish to integrate into an existing project,
you can follow the manual installation steps for [iOS](/crashlytics/usage/installation/ios) and [Android](/crashlytics/usage/installation/android).

> You may like reading a short article we wrote that explains how to configure and _most importantly_ verify your crashlytics installation so you are sure it is working. https://invertase.io/blog/react-native-firebase-crashlytics-configuration

# Platform support and New Architecture

|                      |                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| **Platforms**        | Android, iOS (native Firebase SDK)                                                             |
| **New Architecture** | **Required** from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement). |

> **React Native only:** There is no firebase-js-sdk web equivalent for Crashlytics.

# What does it do

Crashlytics helps you to collect analytics and details about crashes and errors that occur in your app. It does this through three aspects:

- **Logs**: Log events in your app to be sent with the crash report for context if your app crashes.
- **Crash reports**: Every crash is automatically turned into a crash report and sent.
- **Stack traces**: Even when an error is caught and your app recovers, the JavaScript stack trace can still be sent.

<YouTube id="k_mdNRZzd30" />

To learn more, view the [Firebase Crashlytics documentation](https://firebase.google.com/docs/crashlytics?utm_source=invertase&utm_medium=react-native-firebase&utm_campaign=crashlytics).

# Usage

Use the `log` method throughout your app to accumulate extra context for possible crashes that can happen. For additional context, Crashlytics also offers [various methods](/crashlytics/usage#crash-attributes) to set attributes for the crash report. You can also test Crashlytics by forcing a crash through the `crash` method.

Crashlytics also supports sending JavaScript stack traces to the Firebase console. This can be used in any situation where an error occurs but is caught by your own code to recover gracefully. To send a stack trace, pass a JavaScript Error to the `recordError` method.

> Crash reporting is disabled by default whilst developing. To enable this, view the [enable debug crash logs](/crashlytics/usage#enable-debug-crash-logs) documentation.

## Crash Attributes

There are various methods to set attributes for the crash report, in order to provide analytics for crashes and help you review them. You can use set methods to set predefined attributes, but you can also set your own custom attributes.

```js
import React, { useEffect } from 'react';
import { View, Button } from 'react-native';
import {
  getCrashlytics,
  log,
  setUserId,
  setAttribute,
  setAttributes,
  crash,
} from '@react-native-firebase/crashlytics';

async function onSignIn(user) {
  const crashlytics = getCrashlytics();
  log(crashlytics, 'User signed in.');
  await Promise.all([
    setUserId(crashlytics, user.uid),
    setAttribute(crashlytics, 'credits', String(user.credits)),
    setAttributes(crashlytics, {
      role: 'admin',
      followers: '13',
      email: user.email,
      username: user.username,
    }),
  ]);
}

export default function App() {
  useEffect(() => {
    log(getCrashlytics(), 'App mounted.');
  }, []);

  return (
    <View>
      <Button
        title="Sign In"
        onPress={() =>
          onSignIn({
            uid: 'Aa0Bb1Cc2Dd3Ee4Ff5Gg6Hh7Ii8Jj9',
            username: 'Joaquin Phoenix',
            email: 'phoenix@example.com',
            credits: 42,
          })
        }
      />
      <Button title="Test Crash" onPress={() => crash(getCrashlytics())} />
    </View>
  );
}
```

## Error Reports

Even if you catch unexpected errors, in order for your app to recover and behave smoothly you can still report them through
Crashlytics using the `recordError` method. This will also provide you with the associated stack trace.

```jsx
import React, { useState, useEffect } from 'react';
import { View, Text } from 'react-native';
import { getCrashlytics, log, recordError } from '@react-native-firebase/crashlytics';

const users = [];

export default function App() {
  const [userCounts, setUserCounts] = useState(null);

  function updateUserCounts() {
    const crashlytics = getCrashlytics();
    log(crashlytics, 'Updating user count.');
    try {
      if (users) {
        setUserCounts(userCounts.push(users.length));
      }
    } catch (error) {
      recordError(crashlytics, error);
      console.log(error);
    }
  }

  useEffect(() => {
    log(getCrashlytics(), 'App mounted.');
    if (users == true) setUserCounts([]);
    updateUserCounts();
  }, []);

  if (userCounts) {
    return (
      <View>
        <Text>There are currently {userCounts[userCounts.length - 1]} users.</Text>
      </View>
    );
  }

  return (
    <View>
      <Text>Unable to display user information.</Text>
    </View>
  );
}
```

## Opt-out

As Crashlytics will be sending certain information regarding the user, users may want to opt-out of the crash reporting.
This can be done throughout the app with a simple method call to `setCrashlyticsCollectionEnabled`:

```jsx
import React, { useState } from 'react';
import { View, Button, Text } from 'react-native';
import {
  getCrashlytics,
  setCrashlyticsCollectionEnabled,
  crash,
} from '@react-native-firebase/crashlytics';

export default function App() {
  const [enabled, setEnabled] = useState(getCrashlytics().isCrashlyticsCollectionEnabled);

  async function toggleCrashlytics() {
    const crashlytics = getCrashlytics();
    await setCrashlyticsCollectionEnabled(crashlytics, !enabled);
    setEnabled(crashlytics.isCrashlyticsCollectionEnabled);
  }

  return (
    <View>
      <Button title="Toggle Crashlytics" onPress={toggleCrashlytics} />
      <Button title="Crash" onPress={() => crash(getCrashlytics())} />
      <Text>Crashlytics is currently {enabled ? 'enabled' : 'disabled'}</Text>
    </View>
  );
}
```

# firebase.json

## Disable Auto Collection

Additionally, you can configure whether Crashlytics sends out any reports through the `auto_collection_enabled` option in
your `firebase.json` config. If you want users to opt-in, it is recommended that you disable this here and enable it later
through the method once they opt-in.

```json
// <project-root>/firebase.json
{
  "react-native": {
    "crashlytics_auto_collection_enabled": false
  }
}
```

## Enable debug crash logs

Because you have stack traces readily available while you're debugging your app, Crashlytics is disabled by default in debug mode. You can set Crashlytics to be enabled regardless of debug mode through the `debug_enabled` option in your `firebase.json`.

```json
// <project-root>/firebase.json
{
  "react-native": {
    "crashlytics_debug_enabled": true
  }
}
```

## Crashlytics NDK

React Native Firebase supports [Crashlytics NDK](https://firebase.google.com/docs/crashlytics/ndk-reports) reporting
which is enabled by default but will require a change as described in that link to enable symbol upload.

This allows Crashlytics to capture crashes originating from the Yoga layout engine used by React Native.

You can disable Crashlytics NDK in your `firebase.json` config.

```json
// <project-root>/firebase.json
{
  "react-native": {
    "crashlytics_ndk_enabled": false
  }
}
```

## Crashlytics Javascript stacktrace issue generation

React Native Crashlytics module by default installs a global javascript exception handler, and it records a crash with a javascript stack trace any time an unhandled javascript exception is thrown. Sometimes it is not desirable behavior since it might duplicate issues in combination with the default mode of javascript global exception handler chaining. We recommend leaving JS crashes enabled and turning off exception handler chaining. However, if you have special crash handling requirements, you may disable this behavior by setting the appropriate option to false:

```json
// <project-root>/firebase.json
{
  "react-native": {
    "crashlytics_is_error_generation_on_js_crash_enabled": false
  }
}
```

## Crashlytics Javascript exception handler chaining

React Native Crashlytics module's global javascript exception handler by default chains to any previously installed global javascript exception handler after logging the crash with the javascript stack trace. In default react-native setups, this means in development you will then see a "red box" and in release mode you will see a second native crash in the Crashlytics console with no javascript stack trace. These duplicate crash reports are probably not desirable, and the one from the chained handler will not have the javascript stack trace. We recommend disabling this once Crashlytics is integrated in testing. It is enabled by default for easier initial integration testing and to be sure introducing the option was not a breaking change. You may disable exception handler chaining by setting the appropriate option to false:

```json
// <project-root>/firebase.json
{
  "react-native": {
    "crashlytics_javascript_exception_handler_chaining_enabled": false
  }
}
```

## Crashlytics non-fatal exceptions native handling

In case you need to log non-fatal (handled) exceptions on the native side (e.g from `try catch` block), you may use the following static methods:

### Android

```java
import io.invertase.firebase.crashlytics.ReactNativeFirebaseCrashlyticsNativeHelper;
//...

try {
  //...
} catch (Exception e) {
  ReactNativeFirebaseCrashlyticsNativeHelper.recordNativeException(e);
  return null;
}
```

### iOS

```objectivec
#import <RNFBCrashlytics/RNFBCrashlyticsNativeHelper.h>
//...

@try {
  //...
} @catch (NSException *exception) {
  NSMutableDictionary * info = [NSMutableDictionary dictionary];
  [info setValue:exception.name forKey:@"ExceptionName"];
  [info setValue:exception.reason forKey:@"ExceptionReason"];
  [info setValue:exception.callStackReturnAddresses forKey:@"ExceptionCallStackReturnAddresses"];
  [info setValue:exception.callStackSymbols forKey:@"ExceptionCallStackSymbols"];
  [info setValue:exception.userInfo forKey:@"ExceptionUserInfo"];

  NSError *error = [[NSError alloc] initWithDomain:yourdomain code:errorcode userInfo:info];
  [RNFBCrashlyticsNativeHelper recordNativeError:error];
}

```
```

### Offline Support

Source: https://rnfirebase.io/database/offline-support

```mdx

The Realtime Database provides support for offline environments. By default, data will be stored locally on your device
and automatically managed by the Firebase SDKs.

# Enabling Persistence

Persistence is disabled by default when using Realtime Database, however it
[can be changed to be enabled by default in the firebase.json](/database/usage#enabling-persistence). You can also enable persistence programmatically, by calling `setPersistenceEnabled`
as early on in your application code as possible:

```js
// index.js
import { AppRegistry } from 'react-native';
import { getDatabase, setPersistenceEnabled } from '@react-native-firebase/database';

setPersistenceEnabled(getDatabase(), true);

AppRegistry.registerComponent('app', () => App);
```

# Going offline

The API provides a `goOffline` function to force the Realtime Database SDK to go offline, which can be useful for testing.

```js
import { getDatabase, goOffline } from '@react-native-firebase/database';

goOffline(getDatabase());
```

Once offline, all operations will continue to execute, instead using a local instance of your database to perform writes to.
For example, we write to a record which has a listener whilst offline allowing the listener to be called with the updated data:

```jsx
import React, { useEffect } from 'react';
import { getDatabase, ref, onValue, goOffline, set } from '@react-native-firebase/database';

function App() {
  useEffect(() => {
    const db = getDatabase();
    const userAgeRef = ref(db, '/users/123/age');

    onValue(userAgeRef, snapshot => {
      console.log('Users age: ', snapshot.val());
    });

    goOffline(db);
    set(userAgeRef, 32).then(() => {
      console.log('User updated whilst offline.');
    });
  }, []);
}
```

The above code will first execute the `onValue` listener with data from the remote database.

Once offline, `set` on the reference node will `locally` be set to a new value.

The `onValue` listener
however will now subscribe to the local database and provide the new value.

This provides the ability to write code which works in both an online and offline environment without worrying about
data synchronization.

# Going online

The `goOnline` function re-connects the Realtime Database with the remote database. Any locally written changes performed
whilst offline will be automatically synchronized with the remote database.

```js
import { getDatabase, goOnline } from '@react-native-firebase/database';

goOnline(getDatabase());
```

# Local persistence size

By default Firebase Database will use up to `10MB` of disk space to cache data. If the cache grows beyond this size,
Firebase Database will start removing data that hasn't been recently used. If you find that your application caches too
little or too much data, call `setPersistenceCacheSizeBytes` to update the default cache size:

```js
import { getDatabase, setPersistenceCacheSizeBytes } from '@react-native-firebase/database';

setPersistenceCacheSizeBytes(getDatabase(), 2000000); // 2MB
```
```

### Presence Detection

Source: https://rnfirebase.io/database/presence-detection

```mdx

Realtime Database provides the ability to trigger events on the Firebase servers whenever a device is disconnected. This
could be whenever a user has no access to a network or when they quit the app.

One use-case using this functionality is to build a simple presence detection system, whereby we can hold a realtime list
of users currently online within our application. This could be useful when building a chat application
and you wish to view which of your users are currently online.

# Setup

To get started, we first need a location to store our online users.

To keep things simple we'll assume the user is authenticated (e.g. with [Firebase Authentication](/auth)) so we can use their unique user identifier.

Whenever the application opens, [write a new value](/database/usage#writing-data) on a reference node (e.g. `/online/:userId`):

```jsx
import React, { useEffect } from 'react';
import { getAuth } from '@react-native-firebase/auth';
import { getDatabase, ref, set } from '@react-native-firebase/database';

function App() {
  useEffect(() => {
    // Assuming user is logged in
    const userId = getAuth().currentUser.uid;
    const db = getDatabase();
    const reference = ref(db, `/online/${userId}`);

    // Set the /users/:userId value to true
    set(reference, true).then(() => console.log('Online presence set'));
  }, []);
}
```

Whenever the application boots and can connect to the remote server, the value will be written to the database indicating the user is online.

# On Disconnect

Next we need to remove the value when our user disconnects. Typically you would execute this functionality from the device
itself, however this presents a problem.

If the device suddenly goes offline, or is quit, the app can no longer execute code or connect to Firebase. Luckily, the
Realtime Database API provides a way to execute code on the Firebase servers whenever the connection between an app & server
is lost.

The `onDisconnect` function returns a new [`OnDisconnect`](https://invertase.github.io/react-native-firebase/_react-native-firebase/database/FirebaseDatabaseTypes/OnDisconnect.html) instance. This instance
provides functionality to remove or set data whenever a client disconnects. Using the
[`remove`](https://invertase.github.io/react-native-firebase/_react-native-firebase/database/FirebaseDatabaseTypes/OnDisconnect.html#remove) method we can remove the node on the database if the client disconnects:

```jsx
import React, { useEffect } from 'react';
import { getAuth } from '@react-native-firebase/auth';
import { getDatabase, ref, set, onDisconnect } from '@react-native-firebase/database';

function App() {
  useEffect(() => {
    // Assuming user is logged in
    const userId = getAuth().currentUser.uid;
    const db = getDatabase();
    const reference = ref(db, `/online/${userId}`);

    // Set the /users/:userId value to true
    set(reference, true).then(() => console.log('Online presence set'));

    // Remove the node whenever the client disconnects
    onDisconnect(reference)
      .remove()
      .then(() => console.log('On disconnect function configured.'));
  }, []);
}
```

The above code demonstrates a very simple but powerful way to track which users are currently online.
```

### Realtime Database

Source: https://rnfirebase.io/database/usage

```mdx

# Installation

This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app" module, view the
[Getting Started](/) documentation.

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the database module
yarn add @react-native-firebase/database

# If you're developing your app using iOS, run this command
cd ios/ && pod install && cd ..
```

If you're using an older version of React Native without autolinking support, or wish to integrate into an existing project,
you can follow the manual installation steps for [iOS](/database/usage/installation/ios) and [Android](/database/usage/installation/android).

# Platform support and New Architecture

|                      |                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| **Platforms**        | Android, iOS (native Firebase SDK)                                                             |
| **New Architecture** | **Required** from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement). |

**Platform notes:** `getServerTime`, `setPersistenceEnabled`, and `setPersistenceCacheSizeBytes` are RN-specific. `goOnline` / `goOffline` cross the async native bridge. `forceLongPolling` / `forceWebSockets` throw — transport is native-controlled.

# What does it do

The Realtime Database is a cloud-hosted database. Data is stored as JSON and synchronized in realtime to every connected
client. React Native Firebase provides native integration with the Android & iOS Firebase SDKs, supporting both realtime
data sync and offline capabilities.

<YouTube id="U5aeM5dvUpA" />

To learn more, view the [Firebase Realtime Database documentation](https://firebase.google.com/docs/database?utm_source=invertase&utm_medium=react-native-firebase&utm_campaign=database).

# Usage

## References

A core concept to understanding Realtime Database are references - a reference to a specific node within your database. A node
can be a specific property or sub-nodes.

To create a [`Reference`](https://invertase.github.io/react-native-firebase/_react-native-firebase/database/FirebaseDatabaseTypes/Reference.html), use the `ref` function:

```js
import { getDatabase, ref } from '@react-native-firebase/database';

const db = getDatabase();
const reference = ref(db, '/users/123');
```

NOTE: To get a reference to a database other than a 'us-central1' default database, you must pass the database URL. You can find your Realtime Database URL in the Realtime Database section of the Firebase console.

```js
import { getDatabase, ref } from '@react-native-firebase/database';

const db = getDatabase(undefined, 'https://<databaseName>.<region>.firebasedatabase.app/');
const reference = ref(db, '/users/123');
```

## Reading data

The Realtime Data provides the ability to read the value of a reference as a one-time read, or realtime changes to the node.
When a value is read from the database, the API returns a [`DataSnapshot`](https://invertase.github.io/react-native-firebase/_react-native-firebase/database/FirebaseDatabaseTypes/DataSnapshot.html).

The snapshot includes information such as whether the reference node exists, it's value or any children the node has and more.

### One-time read

To read the value once, use `get`:

```js
import { getDatabase, ref, get } from '@react-native-firebase/database';

const db = getDatabase();
get(ref(db, '/users/123')).then(snapshot => {
  console.log('User data: ', snapshot.val());
});
```

### Realtime changes

To setup an active listener to react to any changes to the node and it's children, use `onValue`:

```js
import { getDatabase, ref, onValue } from '@react-native-firebase/database';

const db = getDatabase();
onValue(ref(db, '/users/123'), snapshot => {
  console.log('User data: ', snapshot.val());
});
```

The event handler will be called straight away with the snapshot data, and further called when any changes to the node
occur.

You can unsubscribe by calling the function returned from `onValue`. This can be used within any `useEffect` hooks to automatically unsubscribe
when the hook needs to unsubscribe itself:

```jsx
import React, { useEffect } from 'react';
import { getDatabase, ref, onValue } from '@react-native-firebase/database';

function User({ userId }) {
  useEffect(() => {
    const db = getDatabase();
    const unsubscribe = onValue(ref(db, `/users/${userId}`), snapshot => {
      console.log('User data: ', snapshot.val());
    });

    // Stop listening for updates when no longer required
    return () => unsubscribe();
  }, [userId]);
}
```

#### Additional events

The above example demonstrates how to subscribe to events whenever a value within the node changes. In some cases, you
may need to only subscribe to events whenever a child node is added/changed/moved/removed. This can be achieved using
a different listener such as [`onChildAdded`](https://invertase.github.io/react-native-firebase/_react-native-firebase/database/FirebaseDatabaseTypes/EventType.html).

If you are listening to a node with many children, only listening to data you care about helps reduce network bandwidth
and speeds up your application.

```jsx
import React, { useEffect } from 'react';
import { getDatabase, ref, onChildAdded } from '@react-native-firebase/database';

function User({ userId }) {
  useEffect(() => {
    const db = getDatabase();
    const unsubscribe = onChildAdded(ref(db, '/users'), snapshot => {
      console.log('A new node has been added', snapshot.val());
    });

    // Stop listening for updates when no longer required
    return () => unsubscribe();
  }, [userId]);
}
```

### Querying

Realtime Database provides support for basic querying of your data. When a reference node contains children, you can both
order & limit the returned results.

If your application requires more advanced query capabilities, it is recommended you use [Cloud Firestore](/firestore).

#### Ordering

By default, results are ordered based on the node [keys](/database/usage#database-keys). If however you are using custom keys you can use
the `orderByX` query constraints with `query` to order your data.

For example, if all of the nodes children are scalar values (string, number or boolean) you can use `orderByValue`,
and Firebase will automatically order the results. The example below would return the `def` node before the `abc` node:

```js
/*
 * {
 *   'scores': {
 *     'abc: 30,
 *     'def': 50,
 *   }
 * }
 */

import { getDatabase, ref, query, orderByValue, get } from '@react-native-firebase/database';

const db = getDatabase();
const scores = get(query(ref(db, 'scores'), orderByValue()));
```

Please note that the ordering will not be respected if you do not use the `forEach` method provided on the `DataSnapshot`.

#### Limiting

You can limit the number of results returned from a query by using `limitToFirst` or `limitToLast` query constraints. For example, to limit to the
first 10 results:

```js
import { getDatabase, ref, query, limitToFirst, get } from '@react-native-firebase/database';

const db = getDatabase();
const users = get(query(ref(db, 'users'), limitToFirst(10)));
```

Firebase also provides the ability to return the last set of results in a query via `limitToLast`.

Instead of limiting to a specific number of documents, you can also start from, or end at a specific reference node value:

```js
import {
  getDatabase,
  ref,
  query,
  orderByChild,
  startAt,
  get,
} from '@react-native-firebase/database';

const db = getDatabase();
await get(query(ref(db, 'users'), orderByChild('age'), startAt(21)));
```

## Writing data

The [Firebase documentation](https://firebase.google.com/docs/database/web/structure-data) provides great examples on best
practices on how to structure your data. We highly recommend reading the guide before building out your database.

### Setting data

The `set` function overwrites all of the existing data at that reference node.
The value can be anything; a string, number, object etc:

```js
import { getDatabase, ref, set } from '@react-native-firebase/database';

const db = getDatabase();
set(ref(db, '/users/123'), {
  name: 'Ada Lovelace',
  age: 31,
}).then(() => console.log('Data set.'));
```

If you set the value to `null`, Firebase will automatically class the node as removed, and delete it from the database.

### Updating data

Rather than overwriting all existing data, the `update` function provides the ability to update any existing data on the reference node.
Firebase will automatically merge the data depending on what currently exists.

```js
import { getDatabase, ref, update } from '@react-native-firebase/database';

const db = getDatabase();
update(ref(db, '/users/123'), {
  age: 32,
}).then(() => console.log('Data updated.'));
```

### Pushing data

Currently the examples have only demonstrated working with known reference node keys (e.g. `/users/123`). In some cases,
you may not have a suitable id or may want Firebase to automatically create a node with a generated key. The `push` function
returns a [`ThenableReference`](https://invertase.github.io/react-native-firebase/_react-native-firebase/database/ThenableReference.html), allowing you to observe a node before it is
sent to remote Firebase database.

`push` will automatically generate a new key if one is not provided:

```js
import { getDatabase, ref, push, set } from '@react-native-firebase/database';

const db = getDatabase();
const newReference = push(ref(db, '/users'));

console.log('Auto generated key: ', newReference.key);

set(newReference, {
  age: 32,
}).then(() => console.log('Data updated.'));
```

The keys generated are ordered to the current time, so the list of items returned from Firebase will be chronologically
sorted by default.

## Removing data

To remove data, use the `remove` function:

```js
import { getDatabase, ref, remove } from '@react-native-firebase/database';

await remove(ref(getDatabase(), '/users/123'));
```

Optionally, you can also set the value of a reference node to `null` to remove it from the database:

```js
import { getDatabase, ref, set } from '@react-native-firebase/database';

await set(ref(getDatabase(), '/users/123'), null);
```

## Transactions

Transactions are a way to always ensure a write occurs with the latest information available on the server. Transactions never
partially apply writes & all writes execute at the end of a successful transaction.

Imagine a scenario whereby an app has the ability to "Like" user posts. Whenever a user presses the "Like" button,
the `/likes/:postId` value (number of likes) on the database increments. Without transactions, we'd first need to
read the existing value and then increment that value in two separate operations.

On a high traffic application, the value on the server could already have changed by the time the operation sets a new value,
causing the actual number to not be consistent.

Transactions remove this issue by atomically updating the value on the server. If the value changes whilst the transaction
is executing, it will retry. This always ensures the value on the server is used rather than the client value.

To execute a new transaction, call `runTransaction`:

```js
import { getDatabase, ref, runTransaction } from '@react-native-firebase/database';

function onPostLike(postId) {
  const db = getDatabase();
  const reference = ref(db, `/likes/${postId}`);

  return runTransaction(reference, currentLikes => {
    if (currentLikes === null) return 1;
    return currentLikes + 1;
  });
}

// When post "567" is liked
onPostLike('567').then(transaction => {
  console.log('New post like count: ', transaction.snapshot.val());
});
```

Once the transaction is successful, a promise is resolved with a value containing whether the operation committed on the remote
database and the new [`DataSnapshot`](https://invertase.github.io/react-native-firebase/_react-native-firebase/database/FirebaseDatabaseTypes/DataSnapshot.html) containing the new value.

# Securing data

It is important that you understand how to write rules in your Firebase console to ensure that your data is secure.
Please follow the Firebase Realtime Database documentation on [security](https://firebase.google.com/docs/database/security)

# Using a secondary database

If the default installed Firebase instance needs to address a different database within the same project, pass the database URL to `getDatabase`.
For example:

```js
import { getDatabase, ref } from '@react-native-firebase/database';

const db = getDatabase(undefined, 'https://path-to-database.firebaseio.com');

ref(db);
```

## Connect to a database of a secondary app

If you want to address a database from a different Firebase project, you will need to create a secondary app first
(Read more on creating a secondary app here: https://rnfirebase.io/app/usage).
For example:

```js
import { initializeApp } from '@react-native-firebase/app';
import { getDatabase, ref } from '@react-native-firebase/database';

// create a secondary app
const secondaryApp = await initializeApp(credentials, config);

const secondaryDb = getDatabase(secondaryApp);

ref(secondaryDb);
```

# firebase.json

## Enabling persistence

The Realtime Database can be set to persist data on the user application to be used by the SDKs for offline usage
and caching. To enable this functionality, update the `database_persistence_enabled` key in the `firebase.json` file:

```json
// <project-root>/firebase.json
{
  "react-native": {
    "database_persistence_enabled": true
  }
}
```

For more on persistence, view the [Offline Support](/database/offline-support) documentation.
```

### Cloud Firestore Emulator

Source: https://rnfirebase.io/firestore/emulator

```mdx

You can test your app and its Firestore implementation with an emulator which is built to mimic the behavior of Cloud Firestore. This means you can connect your app directly to the emulator to perform integration testing or QA without touching production data.

For example, you can connect your app to the emulator to safely read and write documents in testing.

## Running the emulator

To be able to mimic the behavior of Cloud Firestore, you need to run the emulator. This is effectively a server that will receive and send requests in lieu of Cloud Firestore. This is achieved by running the following commands:

```bash
// install the Firebase CLI which will run the emulator
curl -sL firebase.tools | bash

// run this command to start the emulator, it will also install it if this is your first time running the command
firebase emulators:start --only firestore
```

You should see a `firestore-debug.log` file in the directory you ran the command which will have a log of all the requests.

# Connect to emulator from your app

You need to configure the following property as soon as possible in the startup of your application:

```jsx
import '@react-native-firebase/app';
import { connectFirestoreEmulator, getFirestore } from '@react-native-firebase/firestore';

// set the host and the port property to connect to the emulator
// set these before any read/write operations occur to ensure it doesn't affect your Cloud Firestore data!
if (__DEV__) {
  connectFirestoreEmulator(getFirestore(), 'localhost', 8080);
}

const db = getFirestore();
```

# Clear locally stored emulator data

Run the following command to clear the data accumulated locally from using the emulator. Please note that you have to insert your project id in the request at this point `[INSERT YOUR PROJECT ID HERE]`.

```bash
curl -v -X DELETE "http://localhost:8080/emulator/v1/projects/[INSERT YOUR PROJECT ID HERE]/databases/(default)/documents"
```

There are more things that can be achieved with the emulator such as using local rules to test the integrity & security of your database. For further information, please follow the Firebase emulator documentation [here](https://firebase.google.com/docs/emulator-suite).
```

### Pagination

Source: https://rnfirebase.io/firestore/pagination

```mdx

Pagination using cloud firestore may be done in various ways but here's a basic way to do it using the firestore query features:
[`orderBy`, `limit`, `startAfter`]

# Setup state

First, create a list display component with 2 state items; `lastDocument` and `userData`:

```jsx
import React, { useState } from 'react';
import type { Node } from 'react';
import { Text, View, Button, Alert } from 'react-native';

import { collection, getFirestore } from '@react-native-firebase/firestore';

const db = getFirestore();
const usersRef = collection(db, 'Users');

const App: () => Node = () => {
  const [lastDocument, setLastDocument] = useState();
  const [userData, setUserData] = useState([]);
};
```

# `LoadData` function

Next, make a function called `LoadData` that fetches data from `Users` collection, and call it when a `Button` is pressed.

If lastDocument is not assigned (meaning initial load), the function will fetch from the start.
After successful fetch from the collection, store the last snapshot data by `setLastDocument`.

```jsx
import React, { useState } from 'react';
import type { Node } from 'react';
import { Text, View, Button, Alert } from 'react-native';

import {
  collection,
  getDocs,
  getFirestore,
  limit,
  orderBy,
  query,
  startAfter,
} from '@react-native-firebase/firestore';

const db = getFirestore();
const usersRef = collection(db, 'Users');

const App: () => Node = () => {
  const [lastDocument, setLastDocument] = useState();
  const [userData, setUserData] = useState([]);

  function LoadData() {
    console.log('LOAD');
    const constraints = [orderBy('age')]; // sort the data
    if (lastDocument !== undefined) {
      constraints.push(startAfter(lastDocument)); // fetch data following the last document accessed
    }
    constraints.push(limit(3)); // limit to your page size, 3 is just an example

    getDocs(query(usersRef, ...constraints)).then(querySnapshot => {
      setLastDocument(querySnapshot.docs[querySnapshot.docs.length - 1]);
      MakeUserData(querySnapshot.docs);
    });
  }

  return (
    <View>
      {userData}
      <Button
        onPress={() => {
          LoadData();
        }}
        title="Load Next"
      />
    </View>
  );
};
```

# `MakeUserData` function

This is just an example function, alter it to process the data to meet your requirements.
In this specific example, it will replace the userData component with the new data fetched.

```js
function MakeUserData(docs) {
  let templist = []; //[...userData] <- use this instead of [] if you want to save the previous data.
  docs.forEach((doc, i) => {
    console.log(doc._data);
    let temp = (
      <View key={i} style={{ margin: 10 }}>
        <Text>{doc._data.name}</Text>
        <Text>{doc._data.age}</Text>
      </View>
    );
    templist.push(temp);
  });
  setUserData(templist); //replace with the new data
}
```

Now, every time the button is pressed, `Users` collection data will be fetched one page at a time.

# Conclusion

Here's the full example code

```jsx
import React, { useState } from 'react';
import type { Node } from 'react';
import { Text, View, Button, Alert } from 'react-native';

import {
  collection,
  getDocs,
  getFirestore,
  limit,
  orderBy,
  query,
  startAfter,
} from '@react-native-firebase/firestore';

const db = getFirestore();
const usersRef = collection(db, 'Users');

const App: () => Node = () => {
  const [lastDocument, setLastDocument] = useState();
  const [userData, setUserData] = useState([]);

  function LoadData() {
    console.log('LOAD');
    const constraints = [orderBy('age')]; // sort the data
    if (lastDocument !== undefined) {
      constraints.push(startAfter(lastDocument)); // fetch data following the last document accessed
    }
    constraints.push(limit(3)); // limit to your page size, 3 is just an example

    getDocs(query(usersRef, ...constraints)).then(querySnapshot => {
      setLastDocument(querySnapshot.docs[querySnapshot.docs.length - 1]);
      MakeUserData(querySnapshot.docs);
    });
  }

  function MakeUserData(docs) {
    let templist = [];
    docs.forEach((doc, i) => {
      console.log(doc._data);
      let temp = (
        <View key={i} style={{ margin: 10 }}>
          <Text>{doc._data.name}</Text>
          <Text>{doc._data.age}</Text>
        </View>
      );
      templist.push(temp);
    });
    setUserData(templist);
  }

  return (
    <View>
      {userData}
      <Button
        onPress={() => {
          LoadData();
        }}
        title="Load Next"
      />
    </View>
  );
};

export default App;
```
```

### Pipelines

Source: https://rnfirebase.io/firestore/pipelines

```mdx

Firestore **pipeline queries** let you run multi-stage read operations (filter, project, aggregate, vector search, and more) against Cloud Firestore using a fluent, composable API. React Native Firebase exposes the same modular pipeline surface as the [firebase-js-sdk](https://firebase.google.com/docs/reference/js/firestore_pipelines) so you can reuse patterns from web documentation and samples.

Pipeline support in React Native Firebase is marked **@beta** in TypeScript. APIs may evolve as Firebase ships new pipeline features.

<YouTube id="sYY1NweSlEc" />

# What are pipeline queries?

Traditional Firestore queries are collection-scoped and limited to a single query shape. **Pipelines** chain **stages** (for example `where`, `select`, `aggregate`, `findNearest`) on a **source** (collection, collection group, documents list, or an existing query). Each stage transforms the result of the previous stage.

Google's upstream resources:

- [Get started with pipeline queries](https://firebase.google.com/docs/firestore/pipelines/get-started-with-pipelines)
- [firebase-js-sdk pipeline reference](https://firebase.google.com/docs/reference/js/firestore_pipelines)
- [Introducing pipeline operations (Firebase blog, Jan 2026)](https://firebase.blog/posts/2026/01/firestore-enterprise-pipeline-operations/)

For a full list of which firebase-js-sdk exports are available in React Native Firebase today, see [SDK compatibility](/firestore/pipelines/sdk-compatibility).

# Requirements

## Firestore Enterprise edition

Pipeline `execute()` requires a **Firestore Enterprise** database. Standard edition databases reject pipeline execution. Create an Enterprise database in the Firebase console or with the Firebase CLI before testing pipelines in your project.

## Cloud execution (not the local emulator)

The Firestore emulator used for most React Native Firebase e2e tests runs in **Standard** edition mode and does **not** faithfully execute pipeline queries. Plan to develop and test pipelines against a **live Enterprise database** in your Firebase project.

The emulator may gain additional Enterprise pipeline support over time, but React Native Firebase pipeline integration tests today run against a dedicated cloud database (`pipelines-e2e` on the public testing project), not the local emulator.

# Installation

Pipelines ship inside `@react-native-firebase/firestore`. Install the app and Firestore modules as described in [Cloud Firestore usage](/firestore/usage):

```bash
yarn add @react-native-firebase/app @react-native-firebase/firestore
cd ios && pod install
```

Expression helpers and types are imported from the pipelines entry point:

```js
import { getFirestore } from '@react-native-firebase/firestore';
import { field, constant, execute } from '@react-native-firebase/firestore/pipelines';
```

You can also call `getFirestore().pipeline()` on any Firestore instance to start building a pipeline from a source stage.

# Basic example

This example reads documents from a collection, keeps rows where `score` is at least 10, projects two fields, and sorts by score descending. Adjust the database id and collection path for your Enterprise database.

```js
import { getFirestore } from '@react-native-firebase/firestore';
import { field, execute } from '@react-native-firebase/firestore/pipelines';

const db = getFirestore(); // use a named Enterprise database id if needed

const pipeline = db
  .pipeline()
  .collection('books')
  .where(field('score').greaterThanOrEqual(10))
  .select(field('title'), field('score'))
  .sort({ ordering: [{ fieldPath: 'score', direction: 'desc' }] });

const snapshot = await execute(pipeline);
snapshot.results.forEach(row => {
  console.log(row.data());
});
```

`execute()` returns a `PipelineSnapshot` with a `results` array of pipeline rows. Iterate `snapshot.results` or access individual entries by index.

# Supported stages and sources

React Native Firebase supports these **source** types:

| Source            | Description                                                      |
| ----------------- | ---------------------------------------------------------------- |
| `collection`      | Documents in a collection path                                   |
| `collectionGroup` | Documents across a collection id                                 |
| `database`        | Database-scoped pipeline source                                  |
| `documents`       | Explicit document paths                                          |
| `query`           | Pipeline created from an existing Firestore query (`createFrom`) |

These **stage** types are available on the pipeline builder:

| Stage                        | Purpose                                          |
| ---------------------------- | ------------------------------------------------ |
| `where`                      | Filter rows                                      |
| `select`                     | Project fields                                   |
| `addFields` / `removeFields` | Add or drop computed fields                      |
| `sort`                       | Order results                                    |
| `limit` / `offset`           | Paginate                                         |
| `aggregate`                  | Group and reduce                                 |
| `distinct`                   | Deduplicate                                      |
| `findNearest`                | Vector similarity search (requires vector index) |
| `replaceWith`                | Replace row shape                                |
| `sample`                     | Random sample                                    |
| `union`                      | Combine pipelines                                |
| `unnest`                     | Expand array fields into rows                    |

## Vector search with `findNearest`

The `findNearest` stage runs vector similarity search over an indexed embedding field. Use the same **lowercase** `distanceMeasure` values as the [firebase-js-sdk pipeline reference](https://firebase.google.com/docs/reference/js/firestore_pipelines): `'euclidean'`, `'cosine'`, or `'dot_product'`.

Create a [vector index](https://firebase.google.com/docs/firestore/pipelines/get-started-with-pipelines) on your Enterprise database before running this query.

```js
import { getFirestore } from '@react-native-firebase/firestore';
import { execute } from '@react-native-firebase/firestore/pipelines';

const db = getFirestore(); // Enterprise database with a vector index on `embedding`

const queryVector = [1.0, 0.0, 0.0];

const snapshot = await execute(
  db
    .pipeline()
    .collection('products')
    .findNearest({
      field: 'embedding',
      vectorValue: queryVector,
      distanceMeasure: 'euclidean',
      limit: 5,
    })
    .select('name'),
);

snapshot.results.forEach(row => {
  console.log(row.data().name);
});
```

Newer firebase-js-sdk stage helpers such as `subcollection` and `parent` are not yet exposed — see [SDK compatibility](/firestore/pipelines/sdk-compatibility).

# Platform notes

| Platform    | Runtime                       | Notes                                                                        |
| ----------- | ----------------------------- | ---------------------------------------------------------------------------- |
| **Android** | Native Firebase Android SDK   | Full native pipeline execution                                               |
| **iOS**     | Native Firebase iOS SDK       | Some expression helpers are not yet supported on iOS; see compatibility page |
| **macOS**   | firebase-js-sdk (web interop) | Same API shape as web; requires network access to Firestore                  |

On **iOS**, pipelines that use unsupported expression functions fail before execution with a clear error listing the function names. The compatibility page lists the current set.

On **macOS**, pipeline execution goes through the web Firebase SDK bundled for React Native macOS targets. Behavior should match web pipeline queries for the same database and rules.

# TypeScript

Pipeline types and expression helpers are exported from `@react-native-firebase/firestore/pipelines`. The `firestore.pipeline()` method is augmented on `Firestore` when you import the firestore module.

Run `yarn compare:types` in the React Native Firebase repository to see tracked differences between our public types and the firebase-js-sdk pipelines declarations.

# Expression helpers

| Helper                                                       | Description                                      |
| ------------------------------------------------------------ | ------------------------------------------------ |
| [`currentDocument()`](/firestore/pipelines/current-document) | Reference the document currently being processed |
| [`ifNull()`](/firestore/pipelines/if-null)                   | Fallback when a field is null or absent          |
| [`switchOn()`](/firestore/pipelines/switch-on)               | First matching condition / default result        |

See [Pipeline SDK compatibility](/firestore/pipelines/sdk-compatibility) for the full firebase-js-sdk parity matrix.

# Next steps

- Review [SDK compatibility](/firestore/pipelines/sdk-compatibility) before porting firebase-js-sdk pipeline samples.
- Read Firebase's [get started guide](https://firebase.google.com/docs/firestore/pipelines/get-started-with-pipelines) for index setup, vector search, and security rules considerations.
- For classic collection queries, continue with [Cloud Firestore usage](/firestore/usage).
```

### Cloud Firestore

Source: https://rnfirebase.io/firestore/usage

```mdx

# Installation

This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app" module, view the
[Getting Started](/) documentation.

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the firestore module
yarn add @react-native-firebase/firestore

# If you're developing your app using iOS, run this command
cd ios/ && pod install
```

If you're using an older version of React Native without autolinking support, or wish to integrate into an existing project,
you can follow the manual installation steps for [iOS](/firestore/usage/installation/ios) and [Android](/firestore/usage/installation/android).

If you have started to receive a `app:mergeDexDebug` error after adding Cloud Firestore, please read the
[Enabling Multidex](/enabling-multidex) documentation for more information on how to resolve this error.

# Platform support and New Architecture

|                      |                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| **Platforms**        | Android, iOS (native Firebase SDK); macOS via firebase-js-sdk web interop                      |
| **New Architecture** | **Required** from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement). |

**Platform notes:** IndexedDB persistence and `memoryLocalCache` / `persistentLocalCache` are **web only**. Native persistence is controlled by the iOS/Android Firestore SDK. `initializeFirestore` is async on React Native.

# What does it do

Firestore is a flexible, scalable NoSQL cloud database to store and sync data. It keeps your data in sync across client
apps through realtime listeners and offers offline support so you can build responsive apps that work regardless of network
latency or Internet connectivity.

<YouTube id="QcsAb2RR52c" />

# Usage

## Collections & Documents

Cloud Firestore stores data within "documents", which are contained within "collections", and documents can also contain
collections. For example, we could store a list of our users documents within a "Users" collection. The `collection` function
allows us to reference a collection within our code:

```js
import { collection, getFirestore } from '@react-native-firebase/firestore';

const db = getFirestore();
const usersCollection = collection(db, 'Users');
```

The `collection` function returns a [`CollectionReference`](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/types/firestore/CollectionReference.html) class, which provides
properties and methods to query and fetch the data from Cloud Firestore. We can also directly reference a single document
on the collection by calling the `doc` function:

```js
import { collection, doc, getFirestore } from '@react-native-firebase/firestore';

const db = getFirestore();

// Get user document with an ID of ABC
const userDocument = doc(collection(db, 'Users'), 'ABC');
```

The `doc` function returns a [`DocumentReference`](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/types/firestore/DocumentReference.html).

A document can contain different types of data, including scalar values (strings, booleans, numbers), arrays (lists) and
objects (maps) along with specific Cloud Firestore data such as [Timestamps](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/modular/Timestamp.html),
[GeoPoints](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/modular/GeoPoint.html), [Bytes](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/modular/Bytes.html#frombase64string) and more.

## Read Data

Cloud Firestore provides the ability to read the value of a collection or document. This can be read one-time, or provide
realtime updates when the data within a query changes.

### One-time read

To read a collection or document once, call `getDocs` on a [`CollectionReference`](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/types/firestore/CollectionReference.html)
or `getDoc` on a [`DocumentReference`](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/types/firestore/DocumentReference.html):

```js
import { collection, doc, getDoc, getDocs, getFirestore } from '@react-native-firebase/firestore';

const db = getFirestore();
const users = await getDocs(collection(db, 'Users'));
const user = await getDoc(doc(collection(db, 'Users'), 'ABC'));
```

### Realtime changes

To setup an active listener to react to any changes to the query, call `onSnapshot` with an event handler callback.
For example, to watch the entire "Users" collection for when any documents are changed (removed, added, modified):

```js
import { collection, getFirestore, onSnapshot } from '@react-native-firebase/firestore';

const db = getFirestore();

function onResult(QuerySnapshot) {
  console.log('Got Users collection result.');
}

function onError(error) {
  console.error(error);
}

onSnapshot(collection(db, 'Users'), onResult, onError);
```

`onSnapshot` also returns a function, allowing you to unsubscribe from events. This can be used within any
`useEffect` hooks to automatically unsubscribe when the hook needs to unsubscribe itself:

```js
import React, { useEffect } from 'react';
import { collection, doc, getFirestore, onSnapshot } from '@react-native-firebase/firestore';

const db = getFirestore();

function User({ userId }) {
  useEffect(() => {
    const subscriber = onSnapshot(doc(collection(db, 'Users'), userId), documentSnapshot => {
      console.log('User data: ', documentSnapshot.data());
    });

    // Stop listening for updates when no longer required
    return () => subscriber();
  }, [userId]);
}
```

Realtime changes via `onSnapshot` can be applied to both collections and documents.

### Snapshots

Once a query has returned a result, Firestore returns either a [`QuerySnapshot`](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/types/firestore/QuerySnapshot.html) (for
collection queries) or a [`DocumentSnapshot`](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/types/firestore/DocumentSnapshot.html) (for document queries). These snapshots
provide the ability to view the data, view query metadata (such as whether the data was from local cache), whether the
document exists or not and more.

#### `QuerySnapshot`

A [`QuerySnapshot`](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/types/firestore/QuerySnapshot.html) returned from a collection query allows you to inspect the collection,
such as how many documents exist within it, access to the documents within the collection, any changes since the last query
and more.

To access the documents within a `QuerySnapshot`, call the `forEach` method:

```js
import { collection, getDocs, getFirestore } from '@react-native-firebase/firestore';

const db = getFirestore();

getDocs(collection(db, 'Users')).then(querySnapshot => {
  console.log('Total users: ', querySnapshot.size);

  querySnapshot.forEach(documentSnapshot => {
    console.log('User ID: ', documentSnapshot.id, documentSnapshot.data());
  });
});
```

Each child document of a `QuerySnapshot` is a [`QueryDocumentSnapshot`](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/types/firestore/QueryDocumentSnapshot.html), which
allows you to access specific information about a document (see below).

#### `DocumentSnapshot`

A [`DocumentSnapshot`](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/types/firestore/DocumentSnapshot.html) is returned from a query to a specific document, or as part
of the documents returned via a [`QuerySnapshot`](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/types/firestore/QuerySnapshot.html). The snapshot provides the ability
to view a documents data, metadata and whether a document actually exists.

To view a documents data, call the `data` method on the snapshot:

```js
import { collection, doc, getDoc, getFirestore } from '@react-native-firebase/firestore';

const db = getFirestore();

getDoc(doc(collection(db, 'Users'), 'ABC')).then(documentSnapshot => {
  console.log('User exists: ', documentSnapshot.exists);

  if (documentSnapshot.exists) {
    console.log('User data: ', documentSnapshot.data());
  }
});
```

A snapshot also provides a helper function to easily access deeply nested data within a document. Call the `get` method
with a dot-notated path:

```js
import { collection, doc, getDoc, getFirestore } from '@react-native-firebase/firestore';

const db = getFirestore();

function getUserZipCode(documentSnapshot) {
  return documentSnapshot.get('info.address.zipcode');
}

getDoc(doc(collection(db, 'Users'), 'ABC'))
  .then(documentSnapshot => getUserZipCode(documentSnapshot))
  .then(zipCode => {
    console.log('Users zip code is: ', zipCode);
  });
```

### Querying

Cloud Firestore offers advanced capabilities for querying collections.

#### Filtering

To filter documents within a collection, pass `where` constraints to `query`. Filtering supports
equality checks and "in" queries. For example, to filter users where their age is greater or equal than 18 years old:

```js
import { collection, getDocs, getFirestore, query, where } from '@react-native-firebase/firestore';

const db = getFirestore();

getDocs(query(collection(db, 'Users'), where('age', '>=', 18))).then(querySnapshot => {
  /* ... */
});
```

Cloud Firestore also supports array membership queries. For example, to filter users who speak both English (en) or
French (fr), use the `in` filter:

```js
getDocs(query(collection(db, 'Users'), where('languages', 'in', ['en', 'fr']))).then(
  querySnapshot => {
    /* ... */
  },
);
```

To learn more about all of the querying capabilities Cloud Firestore has to offer, view the
[Firebase documentation](https://firebase.google.com/docs/firestore/query-data/queries).

You can combine multiple filters using `and()` and `or()` composite constraints.
For example:

```js
import {
  and,
  collection,
  getDocs,
  getFirestore,
  or,
  query,
  where,
} from '@react-native-firebase/firestore';

const db = getFirestore();

const snapshot = await getDocs(
  query(
    collection(db, 'Users'),
    where('user', '==', 'Tim'),
    where('email', '==', 'tim@example.com'),
  ),
);
```

You can use the `and()` function to make logical AND queries:

```js
const snapshot = await getDocs(
  query(
    collection(db, 'Users'),
    and(where('user', '==', 'Tim'), where('email', '==', 'tim@example.com')),
  ),
);
```

You can use the `or()` function to make logical OR queries:

```js
const snapshot = await getDocs(
  query(
    collection(db, 'Users'),
    or(
      and(where('user', '==', 'Tim'), where('email', '==', 'tim@example.com')),
      and(where('user', '==', 'Dave'), where('email', '==', 'dave@example.com')),
    ),
  ),
);
```

For an understanding of what queries are possible, please consult the query limitation documentation on the official
[Firebase Firestore documentation](https://firebase.google.com/docs/firestore/query-data/queries#limits_on_or_queries).

#### Limiting

To limit the number of documents returned from a query, use the `limit` constraint:

```js
import {
  collection,
  getDocs,
  getFirestore,
  limit,
  query,
  where,
} from '@react-native-firebase/firestore';

const db = getFirestore();

getDocs(query(collection(db, 'Users'), where('age', '>=', 18), limit(20))).then(querySnapshot => {
  /* ... */
});
```

The above example both filters the users by age and limits the documents returned to 20.

#### Ordering

To order the documents by a specific value, use the `orderBy` constraint:

```js
import {
  collection,
  getDocs,
  getFirestore,
  orderBy,
  query,
} from '@react-native-firebase/firestore';

const db = getFirestore();

getDocs(query(collection(db, 'Users'), orderBy('age', 'desc'))).then(querySnapshot => {
  /* ... */
});
```

The above example orders all user in the snapshot by age in descending order.

#### Start/End

To start and/or end the query at a specific point within the collection, you can pass either a value to `startAt`,
`endAt`, `startAfter` or `endBefore`. You must specify an order to use pointers, for example:

```js
import {
  collection,
  endAt,
  getDocs,
  getFirestore,
  orderBy,
  query,
  startAt,
} from '@react-native-firebase/firestore';

const db = getFirestore();

getDocs(query(collection(db, 'Users'), orderBy('age', 'desc'), startAt(18), endAt(30))).then(
  querySnapshot => {
    /* ... */
  },
);
```

The above query orders the users by age in descending order, but only returns users whose age is between 18 and 30.

You can further specify a [`DocumentSnapshot`](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/types/firestore/DocumentSnapshot.html) instead of a specific value. For example:

```js
import {
  collection,
  doc,
  getDoc,
  getDocs,
  getFirestore,
  orderBy,
  query,
  startAt,
} from '@react-native-firebase/firestore';

const db = getFirestore();
const userDocumentSnapshot = await getDoc(doc(collection(db, 'Users'), 'DEF'));

getDocs(query(collection(db, 'Users'), orderBy('age', 'desc'), startAt(userDocumentSnapshot))).then(
  querySnapshot => {
    /* ... */
  },
);
```

The above query orders the users by age in descending order, however only returns documents whose order starts at the user
with an ID of `DEF`.

#### Query Limitations

Cloud Firestore does not support the following types of queries:

- Queries with range filters on different fields, as described in the previous section.

## Writing Data

The [Firebase documentation](https://firebase.google.com/docs/firestore/manage-data/structure-data) provides great examples
on best practices on how to structure your data. We highly recommend reading the guide before building out your database.

For a more in-depth look at what is possible when writing data to Firestore please refer to this [documentation](https://firebase.google.com/docs/firestore/manage-data/add-data)

## Adding documents

To add a new document to a collection, use `addDoc` with a [`CollectionReference`](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/types/firestore/CollectionReference.html):

```js
import { addDoc, collection, getFirestore } from '@react-native-firebase/firestore';

const db = getFirestore();

addDoc(collection(db, 'Users'), {
  name: 'Ada Lovelace',
  age: 30,
}).then(() => {
  console.log('User added!');
});
```

`addDoc` adds the new document to your collection with a random unique ID. If you'd like to specify your own ID,
call `setDoc` with a [`DocumentReference`](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/types/firestore/DocumentReference.html) instead:

```js
import { collection, doc, getFirestore, setDoc } from '@react-native-firebase/firestore';

const db = getFirestore();

setDoc(doc(collection(db, 'Users'), 'ABC'), {
  name: 'Ada Lovelace',
  age: 30,
}).then(() => {
  console.log('User added!');
});
```

### Updating documents

The `setDoc` example above replaces any existing data on a given [`DocumentReference`](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/types/firestore/DocumentReference.html).
if you'd like to update a document instead, use `updateDoc`:

```js
import { collection, doc, getFirestore, updateDoc } from '@react-native-firebase/firestore';

const db = getFirestore();

updateDoc(doc(collection(db, 'Users'), 'ABC'), {
  age: 31,
}).then(() => {
  console.log('User updated!');
});
```

The method also provides support for updating deeply nested values via dot-notation:

```js
import { collection, doc, getFirestore, updateDoc } from '@react-native-firebase/firestore';

const db = getFirestore();

updateDoc(doc(collection(db, 'Users'), 'ABC'), {
  'info.address.zipcode': 94040,
}).then(() => {
  console.log('User updated!');
});
```

#### Field values

Cloud Firestore supports storing and manipulating values on your database, such as [Timestamps](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/modular/Timestamp.html),
[GeoPoints](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/modular/GeoPoint.html), [Bytes](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/modular/Bytes.html#frombase64string) and array management.

To store [`GeoPoint`](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/modular/GeoPoint.html) values, provide the latitude and longitude to a new instance of the
class:

```js
import { doc, GeoPoint, getFirestore, updateDoc } from '@react-native-firebase/firestore';

const db = getFirestore();

updateDoc(doc(db, 'users', 'ABC'), {
  'info.address.location': new GeoPoint(53.483959, -2.244644),
});
```

To store raw [Bytes](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/modular/Bytes.html#frombase64string) (for example of a `Base64` image string), provide the string to the static
`fromBase64String` method on the class:

```js
import { Bytes, doc, getFirestore, updateDoc } from '@react-native-firebase/firestore';

const db = getFirestore();

updateDoc(doc(db, 'users', 'ABC'), {
  'info.avatar': Bytes.fromBase64String('data:image/png;base64,iVBOR...'),
});
```

When storing timestamps, it is recommended you use `serverTimestamp()`. When written to the database, the Firebase servers will write a new timestamp based on their time, rather than the clients. This helps
resolve any data consistency issues with different client timezones:

```js
import { doc, getFirestore, serverTimestamp, updateDoc } from '@react-native-firebase/firestore';

const db = getFirestore();

updateDoc(doc(db, 'users', 'ABC'), {
  createdAt: serverTimestamp(),
});
```

Cloud Firestore also allows for storing arrays. To help manage the values with an array (adding or removing), the API
exposes `arrayUnion` and `arrayRemove` helpers.

To add a new value to an array (if value does not exist, will not add duplicate values):

```js
import { arrayUnion, doc, getFirestore, updateDoc } from '@react-native-firebase/firestore';

const db = getFirestore();

updateDoc(doc(db, 'users', 'ABC'), {
  fcmTokens: arrayUnion('ABCDE123456'),
});
```

To remove a value from the array (if the value exists):

```js
import { arrayRemove, doc, getFirestore, updateDoc } from '@react-native-firebase/firestore';

const db = getFirestore();

updateDoc(doc(db, 'users', 'ABC'), {
  fcmTokens: arrayRemove('ABCDE123456'),
});
```

## Removing data

You can delete documents within Cloud Firestore using the `deleteDoc` method with a [`DocumentReference`](https://invertase.github.io/react-native-firebase/_react-native-firebase/firestore/modular/deleteDoc.html):

```js
import { deleteDoc, doc, getFirestore } from '@react-native-firebase/firestore';

const userDocRef = doc(getFirestore(), 'users/ExampleUser');
deleteDoc(userDocRef).then(() => {
  console.log('User deleted!');
});
```

At this time, you cannot delete an entire collection without use of a Firebase Admin SDK.

> If a document contains any sub-collections, these will not be deleted from database. You must delete
> any sub-collections yourself.

If you need to remove a specific property with a document, rather than the document itself, you can use `deleteField`:

```js
import {
  collection,
  deleteField,
  doc,
  getFirestore,
  updateDoc,
} from '@react-native-firebase/firestore';

const db = getFirestore();

updateDoc(doc(collection(db, 'Users'), 'ABC'), {
  fcmTokens: deleteField(),
});
```

## Transactions

Transactions are a way to always ensure a write occurs with the latest information available on the server. Transactions
never partially apply writes & all writes execute at the end of a successful transaction.

Transactions are useful when you want to update a field's value based on its current value, or the value of some other field.
If you simply want to write multiple documents without using the document's current state, a [batch write](/firestore/usage#batch-write) would be more appropriate.

When using transactions, note that:

- Read operations must come before write operations.
- A function calling a transaction (transaction function) might run more than once if a concurrent edit affects a document that the transaction reads.
- Transaction functions should not directly modify application state (return a value from the `updateFunction`).
- Transactions will fail when the client is offline.

Imagine a scenario whereby an app has the ability to "Like" user posts. Whenever a user presses the "Like" button,
a "likes" value (number of likes) on a "Posts" collection document increments. Without transactions, we'd first need to read
the existing value and then increment that value in two separate operations.

On a high traffic application, the value on the server could already have changed by the time the operation sets a new value,
causing the actual number to not be consistent.

Transactions remove this issue by atomically updating the value on the server. If the value changes whilst the transaction
is executing, it will retry. This always ensures the value on the server is used rather than the client value.

To execute a new transaction, call `runTransaction`:

```js
import { doc, getFirestore, runTransaction } from '@react-native-firebase/firestore';

const db = getFirestore();

function onPostLike(postId) {
  // Create a reference to the post
  const postReference = doc(db, 'posts', postId);

  return runTransaction(db, async transaction => {
    // Get post data first
    const postSnapshot = await transaction.get(postReference);

    if (!postSnapshot.exists) {
      throw 'Post does not exist!';
    }

    transaction.update(postReference, {
      likes: postSnapshot.data().likes + 1,
    });
  });
}

onPostLike('ABC')
  .then(() => console.log('Post likes incremented via a transaction'))
  .catch(error => console.error(error));
```

## Batch write

If you do not need to read any documents in your operation set, you can execute multiple write operations as a single batch
that contains any combination of `setDoc`, `updateDoc`, or `deleteDoc` operations. A batch of writes completes atomically and can
write to multiple documents.

First, create a new batch instance via `writeBatch`, perform operations on the batch and finally commit it once ready.
The example below shows how to delete all documents in a collection in a single operation:

```js
import { collection, getDocs, getFirestore, writeBatch } from '@react-native-firebase/firestore';

const db = getFirestore();

async function massDeleteUsers() {
  // Get all users
  const usersQuerySnapshot = await getDocs(collection(db, 'Users'));

  // Create a new batch instance
  const batch = writeBatch(db);

  usersQuerySnapshot.forEach(documentSnapshot => {
    batch.delete(documentSnapshot.ref);
  });

  return batch.commit();
}

massDeleteUsers().then(() => console.log('All users deleted in a single batch operation.'));
```

## Secure your data

It is important that you understand how to write rules in your Firebase console to ensure that your data is secure. Please
follow the Firebase Firestore documentation on [security](https://firebase.google.com/docs/firestore/security/get-started).

## Offline Capabilities

Firestore provides out of the box support for offline capabilities. When reading and writing data, Firestore uses a local
database which synchronizes automatically with the server. Firestore functionality continues when users are offline, and
automatically handles data migration to the server when they regain connectivity.

This functionality is enabled by default, however it can be disabled if you need it to be disabled (e.g. on apps containing
sensitive information). `initializeFirestore` must be called before any Firestore interaction is performed, otherwise it will only take effect on the next app launch:

```js
import { getApp } from '@react-native-firebase/app';
import { initializeFirestore } from '@react-native-firebase/firestore';

async function bootstrap() {
  await initializeFirestore(getApp(), {
    persistence: false, // disable offline persistence
  });
}
```

## Data bundles

Cloud Firestore data bundles are static data files built by you from Cloud Firestore document and query snapshots,
and published by you on a CDN, hosting service or other solution. Once a bundle is loaded, a client app can query documents
from the local cache or the backend.

To load and query data bundles, use `loadBundle` and `namedQuery`:

```js
import {
  getDocsFromCache,
  getFirestore,
  loadBundle,
  namedQuery,
} from '@react-native-firebase/firestore';

const db = getFirestore();

// load the bundle contents
const response = await fetch('https://api.example.com/bundles/latest-stories');
const bundle = await response.text();
await loadBundle(db, bundle);

// query the results from the cache
// note: use getDocsFromCache to query the local cache only
const storiesQuery = await namedQuery(db, 'latest-stories-query');
if (storiesQuery) {
  const snapshot = await getDocsFromCache(storiesQuery);
}
```

You can build data bundles with the Admin SDK. For more information about building and serving data bundles, see Firebase Firestore main documentation on [Data bundles](https://firebase.google.com/docs/firestore/bundles) as well as their "[Bundle Solutions](https://firebase.google.com/docs/firestore/solutions/serve-bundles)" page
```

### Usage with FlatLists

Source: https://rnfirebase.io/firestore/usage-with-flatlists

```mdx

Cloud Firestore provides out of the box support for subscribing to [realtime changes](/firestore/usage#realtime-changes)
on a collection of documents. Whilst building apps with Cloud Firestore, you can easily display lists of a collections
documents using a [`FlatList`](https://reactnative.dev/docs/flatlist.html).

A `FlatList` accepts an array of data, and displays the results in a performance friendly scrollable list. By integrating
a realtime listener with the `FlatList`, whenever data changes without our database it'll automatically and efficiently update
on our application.

# Setup state

First, setup a component which will display the list of data. The component will have 2 separate states; `loading` and
`users`:

```jsx
import React, { useState } from 'react';
import { ActivityIndicator } from 'react-native';

function Users() {
  const [loading, setLoading] = useState(true); // Set loading to true on component mount
  const [users, setUsers] = useState([]); // Initial empty array of users

  if (loading) {
    return <ActivityIndicator />;
  }

  // ...
}
```

# `useEffect` hook

Next, we'll setup a hook with `useEffect`. This hook will trigger when our components mount, and we'll then subscribe to
the "Users" collection documents:

```jsx
import React, { useState, useEffect } from 'react';
import { ActivityIndicator } from 'react-native';
import { collection, getFirestore, onSnapshot } from '@react-native-firebase/firestore';

const db = getFirestore();

function Users() {
  const [loading, setLoading] = useState(true); // Set loading to true on component mount
  const [users, setUsers] = useState([]); // Initial empty array of users

  useEffect(() => {
    const subscriber = onSnapshot(collection(db, 'Users'), () => {
      // see next step
    });

    // Unsubscribe from events when no longer in use
    return () => subscriber();
  }, []);

  if (loading) {
    return <ActivityIndicator />;
  }

  // ...
}
```

# Transforming data

With our event handler setup, we can now iterate over the collection documents. Whilst iterating, we need to create an
array of data a `FlatList` accepts. At a minimum, this is an object with a unique `key` property. For this unique property,
we can use the `id` of a document:

```js
useEffect(() => {
  const subscriber = onSnapshot(collection(db, 'Users'), querySnapshot => {
    const users = [];

    querySnapshot.forEach(documentSnapshot => {
      users.push({
        ...documentSnapshot.data(),
        key: documentSnapshot.id,
      });
    });

    setUsers(users);
    setLoading(false);
  });

  // Unsubscribe from events when no longer in use
  return () => subscriber();
}, []);
```

Once the initial set of documents is returned, we update the `users` state with our raw object data and set the `loading`
state to `false`. We can now

# Integration

With the raw user data in local state, we can now pass this to the `FlatList`:

```jsx
import React, { useState, useEffect } from 'react';
import { ActivityIndicator, FlatList, View, Text } from 'react-native';
import { collection, getFirestore, onSnapshot } from '@react-native-firebase/firestore';

const db = getFirestore();

function Users() {
  // ...

  if (loading) {
    return <ActivityIndicator />;
  }

  return (
    <FlatList
      data={users}
      renderItem={({ item }) => (
        <View style={{ height: 50, flex: 1, alignItems: 'center', justifyContent: 'center' }}>
          <Text>User ID: {item.id}</Text>
          <Text>User Name: {item.name}</Text>
        </View>
      )}
    />
  );
}
```

With little effort, our list will automatically update in realtime whenever a document is added/removed/modified!
This functionality can be further manipulated to respond to user filters via [Querying](/firestore/usage#querying) if required.
```

### Cloud Functions

Source: https://rnfirebase.io/functions/usage

```mdx

# Installation

This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app" module, view the
[Getting Started](/) documentation.

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the functions module
yarn add @react-native-firebase/functions

# If you're developing your app using iOS, run this command
cd ios/ && pod install
```

If you're using an older version of React Native without autolinking support, or wish to integrate into an existing project,
you can follow the manual installation steps for [iOS](/functions/usage/installation/ios) and [Android](/functions/usage/installation/android).

# Platform support and New Architecture

|                      |                                                                                                                                                                                                                                  |
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Platforms**        | Android, iOS (native Firebase SDK)                                                                                                                                                                                               |
| **New Architecture** | **Required** — since v24 for Functions, and for all native modules from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement) and [Migrating to v24 — Cloud Functions](/migrating-to-v24#cloud-functions). |

# What does it do

Firebase Cloud Functions let you automatically run backend code in response to events triggered by Firebase features and
HTTPS requests. Your code is stored in Google's cloud and runs in a managed environment. There's no need to manage and
scale your own servers.

<YouTube id="vr0Gfvp5v1A" />

After you write and deploy a function, Google's servers begin to manage the function immediately. You can fire the function
directly with an HTTP request, via the Cloud Functions module, or in the case of background functions, Google's servers will listen for events and run
the function when it is triggered.

For more information on use cases, view the [Firebase Cloud Functions](https://firebase.google.com/docs/functions/use-cases) documentation.

# Usage

The Cloud Functions module provides the functionality to directly trigger deployed HTTPS callable functions, without worrying
about security or implementing a HTTP request library.

Functions deployed to Firebase have unique names, allowing you to easily identify which endpoint you wish to send a request to.
To learn more about deploying Functions to Firebase, view the [Writing & Deploying Functions](/functions/writing-deploying-functions) documentation.

## Using an emulator

Whilst developing your application with Cloud Functions, it is possible to run the functions inside of a local emulator.

To call the emulated functions in the **default** region, call the `useEmulator` method exposed by the library:

```js
import { connectFunctionsEmulator, getFunctions } from '@react-native-firebase/functions';

if (__DEV__) {
  connectFunctionsEmulator(getFunctions(), 'localhost', 5001);
}
```

If your functions are deployed on a different region, then please see [Region-specific Functions](/functions/usage#region-specific-functions)

## Calling an endpoint

Assuming we have a deployed a callable endpoint named `listProducts`, to call the endpoint the library exposes a
`httpsCallable` method. For example:

```js
// Deployed HTTPS callable
exports.listProducts = functions.https.onCall(() => {
  return [
    /* ... */
    // Return some data
  ];
});
```

Within the React Native application, the list of products returned can be directly accessed:

```jsx
import { getFunctions, httpsCallable } from '@react-native-firebase/functions';

function App() {
  const [loading, setLoading] = useState(true);
  const [products, setProducts] = useState([]);

  useEffect(() => {
    httpsCallable(getFunctions(), 'listProducts')().then(response => {
      setProducts(response.data);
      setLoading(false);
    });
  }, []);

  if (loading) {
    return null;
  }

  // ...
}
```

## Region-specific Functions

If you need to deploy Functions in a region other than the default one, modified statements need to be used.

```javascript
import {
  connectFunctionsEmulator,
  getFunctions,
  httpsCallable,
} from '@react-native-firebase/functions';
import { getApp } from '@react-native-firebase/app';

connectFunctionsEmulator(getFunctions(getApp(), 'region_name'), 'localhost', 5001);

httpsCallable(getFunctions(getApp(), 'region_name'), 'listProducts')({ abc: 123 }).then();
```
```

### Writing & Deploying Cloud Functions

Source: https://rnfirebase.io/functions/writing-deploying-functions

```mdx

Cloud Functions are a powerful asset to a developers workflow, allowing you to build complex backend tasks with
minimal maintenance overhead. The following page outlines the steps required for writing, testing & deploying Cloud Functions to your Firebase project.

## Environment Setup

Firebase provides a CLI which is required to build and deploy Cloud Functions. To install the CLI, install the `firebase-tools` package globally on your computer from your terminal:

```bash
npm install -g firebase-tools
```

Once installed, login to Firebase with the CLI. This process will automatically open a browser instance giving you the ability to login to your Firebase account.

```bash
firebase login
```

Once logged in, create a new directory on your development environment. This will be used as our working directory
where our Cloud Functions will be written and deployed from. Within this directory, run the following command from your
terminal to initialize a new project structure:

```bash
firebase init functions
```

You will be offered two options for language support, for this tutorial select JavaScript. Allow the CLI to install
dependencies using NPM. Once complete your project structure will look like this:

```
myproject
 +- .firebaserc    # Hidden file that helps you quickly switch between
 |                 # projects with `firebase use`
 |
 +- firebase.json  # Describes properties for your project
 |
 +- functions/     # Directory containing all your functions code
      |
      +- .eslintrc.json  # Optional file containing rules for JavaScript linting.
      |
      +- package.json  # NPM package file describing your Cloud Functions code
      |
      +- index.js      # main source file for your Cloud Functions code
      |
      +- node_modules/ # directory where your dependencies (declared in
                       # package.json) are installed
```

## Writing a Function

The Firebase CLI has created a project structure and also installed a number of dependencies which will be used to build our Cloud Functions.

To enable us to mock some fake data to use in the deployed functions, lets use the [`@faker-js/faker`](https://www.npmjs.com/package/@faker-js/faker)
library to create mock data.

```bash
cd functions/
npm install @faker-js/faker
```

Now it's time to write our Cloud Function. Open up the generated `functions/index.js` file in your chosen editor.
The CLI has already imported the `firebase-functions` package required to build a Cloud Function. Firebase uses
[named exports](https://developer.mozilla.org/en-US/docs/web/javascript/reference/statements/export) to help identify
functions. These exports are used to build the API endpoint name which will be accessible from our React Native application.

For this tutorial, we are creating a product listing API. Go ahead and create a new HTTPS callable named function called `listProducts`:

```js
// functions/index.js
const functions = require('firebase-functions');

exports.listProducts = functions.https.onCall((data, context) => {
  // ...
});
```

The `onCall` callback returns two objects:

We can now return an array of products, generated from the `faker` library. As we are mocking a data set, it's important
to keep consistent results for each request. The data should be generated before the request is received, rather than a
new data set being generated on each request:

```js
// functions/index.js
const functions = require('firebase-functions');
const { faker } = require('@faker-js/faker');

// Initialize products array
const products = [];

// Max number of products
const LIMIT = 100;

// Push a new product to the array
for (let i = 0; i < LIMIT; i++) {
  products.push({
    name: faker.commerce.productName(),
    price: faker.commerce.price(),
  });
}

exports.listProducts = functions.https.onCall((data, context) => {
  return products;
});
```

### Testing your function

Before deploying our functions project, we can run the `serve` command which builds a locally accessible instance of our
Cloud Functions. Run the following command from within the `functions/` directory:

```bash
cd functions/
npm run serve
```

Once booted, the CLI will be provide a local web server with the products endpoint openly available, e.g:

```
functions: listProducts: http://localhost:5000/rnfirebase-demo/us-central1/listProducts
```

In your terminal (or browser), access the endpoint provided. Our list of generated products is ready for use.

```
curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X POST -d '{"data":{}}' http://localhost:5000/rnfirebase-demo/us-central1/listProducts
```

### Security

By default the endpoint will be publicly accessible when deployed. Firebase offers an out-of-the-box solution for handling
authentication, which integrates with the [Authentication](/auth) module. To secure our endpoint for authenticated users only, check whether the `auth`
property exists on the function execution context:

```js
exports.listProducts = functions.https.onCall((data, context) => {
  if (!context.auth) {
    throw new functions.https.HttpsError('unauthenticated', 'Endpoint requires authentication!');
  }

  return products;
});
```

When calling the function without authentication, an error response will be returned to the caller.

If the user is authenticated, we can access the users data via the `context.auth` property. For example their unique user identifier will be available by accessing `context.auth.uid`.

### Handling function arguments

A common requirement for endpoints is calling the endpoint with custom parameters. For example, rather than returning a list
of 1000 products, we can paginate the data by passing in arguments when calling our function.

These arguments can be accessed via the `data` property when the function is executed, let's update our function code to support pagination arguments:

```js
exports.listProducts = functions.https.onCall((data, context) => {
  const { page = 1, limit = 10 } = data;

  const startAt = (page - 1) * limit;
  const endAt = startAt + limit;

  return products.slice(startAt, endAt);
});
```

## Deploying Functions

Once your functions are ready to be deployed, the project provides a `deploy` script which will upload all of your code
onto the Firebase infrastructure and automatically provision production ready endpoints. Within the project, run the
`deploy` script from the `functions` directory:

```bash
cd functions/
npm run deploy
```

Once complete, your Cloud Function will also be available from a publicly accessible endpoint if required, for example:

```
https://us-central1-rnfirebase-demo-23aa8.cloudfunctions.net/listProducts
```

### Calling your function

Once your function has been deployed you can now call it through the React Native Firebase Functions SDK in your application:

```js
import { getFunctions, httpsCallable } from '@react-native-firebase/functions';

const { data } = await httpsCallable(
  getFunctions(),
  'listProducts',
)({
  page: 1,
  limit: 15,
});
```
```

### In App Messaging

Source: https://rnfirebase.io/in-app-messaging/usage

```mdx

# Installation

This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app" module, view the
[Getting Started](/) documentation.

This module also requires that the `@react-native-firebase/analytics` module is already setup and installed. To install the "analytics" module, view it's [Getting Started](/analytics/usage) documentation.

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the in-app-messaging module
yarn add @react-native-firebase/in-app-messaging

# If you're developing your app using iOS, run this command
cd ios/ && pod install
```

Note: in-app-messaging requires a minimum android gradle plugin version of 3.5.4 to compile or you will see `AAPT` errors regarding unexpected XML with `<queries>` elements. However, `react-native@0.63.4` still ships with a default of 3.5.3. If you have not already, you must update the line `classpath("com.android.tools.build:gradle:3.5.3")`in `android/build.gradle` to a minimum of `3.5.4` for android builds to work.

If you're using an older version of React Native without autolinking support, or wish to integrate into an existing project,
you can follow the manual installation steps for [iOS](/in-app-messaging/usage/installation/ios) and [Android](/in-app-messaging/usage/installation/android).

# Platform support and New Architecture

|                      |                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| **Platforms**        | Android, iOS (native Firebase SDK)                                                             |
| **New Architecture** | **Required** from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement). |

> **React Native only:** There is no firebase-js-sdk web equivalent for In-App Messaging.

# What does it do

Firebase In-App Messaging helps you to engage your apps active users by sending them targeted, contextual messages that encourage
them to use key app features. For example, you could send an in-app message to get users to subscribe, watch a video,
complete a level, or buy an item. You can customize messages as cards, banners, modals, or images, and set up triggers
so that they appear exactly when they'd benefit your users most.

<YouTube id="5MRKpvKV2pg" />

React Native Firebase provides support for both native Android & iOS integration with a simple JavaScript API.

# Usage

Most of the set up occurs on [Firebase Console](https://console.firebase.google.com/u/0/project/_/inappmessaging) in the
`In-App Messaging` tab. You can create campaigns and customize elements such as Image, Banner, Modal & Cards to appear on
predefined events (e.g. purchase). This involves no code for the developer to implement. Any published campaigns from the
Firebase Console are automatically handled and displayed on your user's device.

This module provides a JavaScript API to allow greater control of the displaying of these messages.

# Limitations

According to github issue https://github.com/firebase/firebase-ios-sdk/issues/4768 Firebase In-App Messaging allows only 1 campaign per day on app foreground or app launch. This limit is to prevent you from accidentally overwhelming your users with non-contextually appropriate messages. However, if you use the contextual triggers (for example: Analytics event or programmatically triggered in-app-messaging campaigns), there is no daily rate limit.

## Displaying Messages

The `setMessagesDisplaySuppressed` method allows you to control when messages can/cannot be displayed. Below illustrates
a use case for controlling the flow of messages:

> The suppressed state is not persisted between restarts, so ensure it is called as early as possible.

```jsx
import {
  getInAppMessaging,
  setMessagesDisplaySuppressed,
} from '@react-native-firebase/in-app-messaging';

async function bootstrap() {
  await setMessagesDisplaySuppressed(getInAppMessaging(), true);
}

async function onSetup(user) {
  await setupUser(user);
  await setMessagesDisplaySuppressed(getInAppMessaging(), false);
}
```

# firebase.json

## Disable collection of data

In App Messaging can be further configured to enable or disable automatic data collection for Firebase In-App Messaging.

This is useful for opt-in-first data flows, for example when dealing with GDPR compliance. This can be overridden in JavaScript.
This is possible by setting the below noted property on the `firebase.json` file at the root of your project directory.

```json
// <project-root>/firebase.json
{
  "react-native": {
    "in_app_messaging_auto_collection_enabled": false
  }
}
```
```

### Installations

Source: https://rnfirebase.io/installations/usage

```mdx

# Installation

This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app"
module, view the [Getting Started](/) documentation.

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the installations module
yarn add @react-native-firebase/installations

# If you're developing your app using iOS, run this command
cd ios/ && pod install
```

# Platform support and New Architecture

|                      |                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| **Platforms**        | Android, iOS (native Firebase SDK)                                                             |
| **New Architecture** | **Required** from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement). |

> **React Native only:** There is no firebase-js-sdk web equivalent for Installations.

# What does it do

The Firebase installations service:

- provides a unique identifier for a Firebase installation
- provides an auth token for a Firebase installation
- provides an API to perform GDPR-compliant deletion of a Firebase installation.

Each configured `FirebaseApp` has a corresponding single instance of Installations. An instance of the class provides access to the installation info for the FirebaseApp as well as the ability to delete it. A Firebase Installation is unique by `FirebaseApp.name` and `FirebaseApp.options.googleAppID`

# Usage

Please see the API Reference for detailed usage information on the available APIs
```

### iOS Notification Images

Source: https://rnfirebase.io/messaging/ios-notification-images

```mdx

This is a quick guide to display an image in an incoming notification. Android handles this out of the box so this extra setup is **only necessary for iOS**.

> If you want to know more about the specifics of this setup read the [official Firebase docs](https://firebase.google.com/docs/cloud-messaging/ios/send-image).

**🚨 Before you start**
Be sure you already have Cloud Messaging installed and set up. In case you don't [get started here](/messaging/usage).

**🏁 Ready to start**
The following steps will guide you through how to add a new target to your application to support payloads with an image. Open Xcode and let's get started.

### Step 1 - Add a notification service extension

- From Xcode top menu go to: **File > New > Target...**
- A modal will present a list of possible targets, scroll down or use the filter to select `Notification Service Extension`. Press **Next**.
- Add a product name (use `ImageNotification` to follow along) and click **Finish**
- Enable the scheme by clicking **Activate**

![step-1](/assets/messaging/ios-notification-images-step-1.gif)

### Step 2 - Add target to the Podfile

Ensure that your new extension has access to Firebase/Messaging pod by adding it in the Podfile:

- From the Navigator open the Podfile: **Pods > Podfile**
- Scroll down to the bottom of the file and add

> This CocoaPods + static recipe requires opting out of RNFB's default SPM mode (`$RNFirebaseDisableSPM = true`). See [iOS SPM Support](/ios-spm).

```Ruby
target 'ImageNotification' do
  use_frameworks! :linkage => :static
  pod 'Firebase/Messaging'
end
```

- Install or update your pods using `pod install` from the `ios` folder

![step-2](/assets/messaging/ios-notification-images-step-2.gif)

### Step 3 - Use the extension helper (Objective-C)

> If you selected to create your extension as a Swift project, jump to the next section.

At this point everything should still be running normally. This is the final step which is invoking the extension helper.

- From the navigator select your `ImageNotification` extension
- Open the `NotificationService.m` file
- At the top of the file import `FirebaseMessaging.h` right after the `NotificationService.h` as shown below

```diff
#import "NotificationService.h"
+ #import "FirebaseMessaging.h"
```

- then replace everything from line 25 to 28 with the extension helper

```diff
- // Modify the notification content here...
- self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title];

- self.contentHandler(self.bestAttemptContent);
+ [[FIRMessaging extensionHelper] populateNotificationContent:self.bestAttemptContent withContentHandler:contentHandler];
```

![step-3](/assets/messaging/ios-notification-images-step-3.gif)

### Step 3 - Use the extension helper (Swift)

At this point everything should still be running normally. This is the final step which is invoking the extension helper.

- From the navigator select your `ImageNotification` extension
- Open the `NotificationService.swift` file
- At the top of the file import `Firebase` right after the `NotificationService` as shown below

```diff
import UserNotifications
+ import Firebase

class NotificationService: UNNotificationServiceExtension {
```

- then replace everything from line 19 to 23 with the extension helper

```diff
        if let bestAttemptContent = bestAttemptContent {
-            // Modify the notification content here...
-            bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
-
-            contentHandler(bestAttemptContent)
+            Messaging.serviceExtension()
+               .populateNotificationContent(bestAttemptContent, withContentHandler: contentHandler)
        }
```

## All done

Run the app and check it builds successfully – **make sure you have the correct target selected**. Now you can use the [Notifications composer](https://console.firebase.google.com/u/0/project/_/notification) to test sending notifications with an image (`300KB` max size). You can also create custom notifications via [`FCM HTTP`](https://firebase.google.com/docs/cloud-messaging/http-server-ref) or [`firebase-admin`](https://www.npmjs.com/package/firebase-admin). Read this page to send [messages from a server](/messaging/server-integration).
```

### iOS Permissions

Source: https://rnfirebase.io/messaging/ios-permissions

```mdx

> **Deprecated:** Notification permission APIs (`requestPermission`, `hasPermission`, `AuthorizationStatus`, and `IOSPermissions`) in `@react-native-firebase/messaging` are deprecated and will be removed in a future major release. Use [react-native-permissions](https://github.com/zoontek/react-native-permissions) or [expo-notifications](https://docs.expo.dev/versions/latest/sdk/notifications/) for notification permission requests on iOS and Android instead. See [issue #6283](https://github.com/invertase/react-native-firebase/issues/6283).

## Understanding permissions

Before diving into requesting notification permissions from your users, it is important to understand how iOS handles permissions.

Notifications cannot be shown to users if the user has not "granted" your application permission. The overall notification permission of a single application can be either be "not determined", "granted" or "declined". Upon installing a new application, the default status is "not determined".

In order to receive a "granted" status, you must request permission from your user (see below). The user can either accept or decline your request to grant permissions. If granted, notifications will be displayed based on the permission settings which were requested.

If the user declines the request, you cannot re-request permission, trying to request permission again will immediately return a "denied" status without any user interaction - instead the user must manually enable notification permissions from the iOS Settings UI.

## Requesting permissions

As explained in the [Usage](/messaging/usage#ios---requesting-permissions) documentation, permission
must be requested from your users in order to display remote notifications from FCM, via the
`requestPermission` API:

```js
import { getMessaging, requestPermission } from '@react-native-firebase/messaging';

async function requestUserPermission() {
  const messaging = getMessaging();
  const authorizationStatus = await requestPermission(messaging);

  if (authorizationStatus) {
    console.log('Permission status:', authorizationStatus);
  }
}
```

Once a user has selected a permission status, iOS prevents the permission dialog from being displayed again. This allows the users of your application full control of how notifications are handled:

- If the user declines permission, they must manually allow notifications via the Settings UI for your application.
- If the user has accepted permission, notifications will be shown using the settings requested (e.g. with or without sound).

### Permission settings

Although overall notification permission can be granted, the permissions can be further broken down into settings.

Settings are used by the device to control notifications behavior, for example alerting the user with sound. When requesting permission, you can provide a custom object of settings if you wish to override the defaults. This is demonstrated in the following example:

```js
import { getMessaging, requestPermission } from '@react-native-firebase/messaging';

const messaging = getMessaging();

await requestPermission(messaging, {
  sound: false,
  announcement: true,
  // ... other permission settings
});
```

The full list of permission settings can be seen in the table below along with their default values:

| Permission                        | Default | Description                                                                                                                                             |
| --------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `alert`                           | `true`  | Sets whether notifications can be displayed to the user on the device.                                                                                  |
| `announcement`                    | `false` | If enabled, Siri will read the notification content out when devices are connected to AirPods.                                                          |
| `badge`                           | `true`  | Sets whether a notification dot will appear next to the app icon on the device when there are unread notifications.                                     |
| `carPlay`                         | `true`  | Sets whether notifications will appear when the device is connected to [CarPlay](https://www.apple.com/ios/carplay/).                                   |
| `provisional`                     | `false` | Sets whether provisional permissions are granted. See [Provisional permission](/messaging/ios-permissions#provisional-permission) for more information. |
| `sound`                           | `true`  | Sets whether a sound will be played when a notification is displayed on the device.                                                                     |
| `providesAppNotificationSettings` | `false` | Indicates the system to display a button for in-app notification settings.                                                                              |

The settings provided will be stored by the device and will be visible in the iOS Settings UI for your application.

If the permission dialog has already been presented to the user and you wish to update the existing permission settings
(e.g. enabling sound), the setting will be silently updated and the `requestPermission` call will instantly resolve without showing a dialog.

#### Reading current status

In some cases, you may wish to read the current permission status. The `requestPermission`
API used above resolves an enum that returns the current status.

For example:

```js
import {
  getMessaging,
  requestPermission,
  AuthorizationStatus,
} from '@react-native-firebase/messaging';

async function checkApplicationPermission() {
  const messaging = getMessaging();
  const authorizationStatus = await requestPermission(messaging);

  if (authorizationStatus === AuthorizationStatus.AUTHORIZED) {
    console.log('User has notification permissions enabled.');
  } else if (authorizationStatus === AuthorizationStatus.PROVISIONAL) {
    console.log('User has provisional notification permissions.');
  } else {
    console.log('User has notification permissions disabled');
  }
}
```

The value returned is a number value, which can be mapped to one of the following values from `AuthorizationStatus`:

- `-1` = `AuthorizationStatus.NOT_DETERMINED`: Permission has not yet been requested for your application.
- `0` = `AuthorizationStatus.DENIED`: The user has denied notification permissions.
- `1` = `AuthorizationStatus.AUTHORIZED`: The user has accept the permission & it is enabled.
- `2` = `AuthorizationStatus.PROVISIONAL`: [Provisional authorization](/messaging/ios-permissions#provisional-authorization) has been granted.
- `3` = `AuthorizationStatus.EPHEMERAL`: The app is authorized to create notifications for a limited amount of time. Used for app clips.

To help improve the chances of the user granting your app permission, it is recommended that permission is requested at a time which makes
sense during the flow of your application (e.g. starting a new chat), where the user would expect to receive such notifications.

It is also possible to fetch the current permission status without requesting permission, by calling the `hasPermission` API instead.

### Provisional authorization

Devices on iOS 12+ can use provisional authorization. This type of permission system allows for notification
permission to be instantly granted without displaying a dialog to your user. The permission allows notifications to be displayed quietly;

- meaning they're only visible within the device notification center.

To enable provisional notifications, pass an object to the `requestPermission` method, with the `provisional` key set to `true`:

```js
import { getMessaging, requestPermission } from '@react-native-firebase/messaging';

await requestPermission(getMessaging(), {
  provisional: true,
});
```

Users can then choose a permission option via the notification itself, and select whether they can continue to display quietly, display prominently or not at all.

### Handle button for in-app notifications settings

Devices on iOS 12+ can provide a button in iOS Notifications Settings _(at OS level: `Settings -> [App name] -> Notifications`)_ to redirect users to in-app notifications settings.

1. Request `providesAppNotificationSettings` permissions:

```typescript
import { getMessaging, requestPermission } from '@react-native-firebase/messaging';

await requestPermission(getMessaging(), { providesAppNotificationSettings: true });
```

2. Handle interaction when app is in background state:

```typescript
// index.js
import { AppRegistry } from 'react-native'
import { getMessaging, setOpenSettingsForNotificationsHandler } from '@react-native-firebase/messaging'

const messaging = getMessaging();

...

setOpenSettingsForNotificationsHandler(messaging, async () => {
    // Set persistent value, using the MMKV package just as an example of how you might do it
    MMKV.setBool(openSettingsForNotifications, true)
})

...

AppRegistry.registerComponent(appName, () => App)
```

```typescript
// App.tsx

const App = () => {
  const [openSettingsForNotifications] = useMMKVStorage('openSettingsForNotifications', MMKV, false)

  useEffect(() => {
    if (openSettingsForNotifications) {
      navigate('NotificationsSettingsScreen')
    }
  }, [openSettingsForNotifications])

  ...
}
```

3. Handle interaction when app is in quit state:

```typescript
// App.tsx
import { getMessaging, getDidOpenSettingsForNotification } from '@react-native-firebase/messaging';

const messaging = getMessaging();

const App = () => {
  useEffect(() => {
        getDidOpenSettingsForNotification(messaging)
            .then(async didOpenSettingsForNotification => {
                if (didOpenSettingsForNotification) {
                    navigate('NotificationsSettingsScreen')
                }
            })
  }, [])

    ...
}
```
```

### Notifications

Source: https://rnfirebase.io/messaging/notifications

```mdx

Notifications are an important tool used on the majority of Android & iOS applications, used to improve user
experience, used to engage users with your application and much more. The Cloud Messaging module provides basic support for
displaying and handling notifications.

> Looking for an advanced local notifications library which integrates with FCM? [Check out Notifee!](https://notifee.app)

# Displaying a Notification

The Firebase Cloud Messaging SDKs for Android and iOS allow for notifications to be displayed on devices when the application
is either quit or in the background. The Firebase Console, Firebase Admin SDKs and REST API all allow a `notification`
property to be attached to a message.

If an incoming message with this property exists, and the app is not currently visible (quit or in the background),
a notification is displayed on the device. However, if the application is in the foreground, an event will be delivered
containing the notification data and no visible notification will be displayed. See the [Usage](/messaging/usage) documentation
to learn more about handling events.

## Via Firebase Console

The [Firebase Console provides a simple UI](https://console.firebase.google.com/project/_/notification) to allow devices
to display a notification. Using the console, you can:

- Send a basic notification with custom text and images.
- Target applications which have been added to your project.
- Schedule notifications to display at a later date.
- Send recurring notifications.
- Assign conversion events for your analytical tracking.
- A/B test user interaction (called "experiments").
- Test notifications on your development devices.

The Firebase Console automatically sends a message to your devices containing a `notification` property which is handled
by the React Native Firebase Cloud Messaging module. See [Handling Interaction](/messaging/notifications#handling-interaction) to learn about how
to support user interaction.

## Via Admin SDKs

The various Firebase Admin SDKs allow you to send messages to your users. If these messages also contain notification
options, the React Native Firebase Cloud Messaging module will automatically display these notifications.

For example, when using the [`firebase-admin`](https://www.npmjs.com/package/firebase-admin) package in a Node.js environment
to send [messages from a server](/messaging/server-integration), a `notification` property can be added to the message payload:

```js
await admin.messaging().sendMulticast({
  tokens: [
    /* ... */
  ], // ['token_1', 'token_2', ...]
  notification: {
    title: 'Basic Notification',
    body: 'This is a basic notification sent from the server!',
    imageUrl: 'https://my-cdn.com/app-logo.png',
  },
});
```

The Cloud Messaging module will intercept these messages and if the `notification` property is available, it will display
a notification on the device (if the app is not in the foreground). Messages sent with both a `notification` and `data` property
will display a notification and also trigger the `onMessage` handlers (see [Usage](/messaging/usage)).

The different Admin SDKs available have similar implementation details on how to add a custom `notification` property to
the FCM payload, however the Cloud Messaging module will handle all requests. To learn more, view the
[Firebase Admin SDK documentation](https://firebase.google.com/docs/reference/admin) for your chosen admin SDK. For those using the
HTTP implementation, view the [REST Cloud Messaging](https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages)
reference.

## Via REST

If you are unable to use a [Firebase Admin SDK](https://firebase.google.com/docs/reference/admin), Firebase also provides
support for sending messages to devices via a POST request:

```HTTP
POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1

Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA

{
   "message":{
      "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
      "data":{},
      "notification":{
        "body":"This is an FCM notification message!",
        "title":"FCM Message"
      }
   }
}
```

To learn more about the REST API, view the [Firebase documentation](https://firebase.google.com/docs/cloud-messaging/send/v1-api),
and select the "REST" tab under the code examples.

# Handling Interaction

When a user interacts with your notification by pressing on it, the default behavior is to open the application (since
notifications via FCM only display when the application is in the background, the application will always open).

In many cases, it is useful to detect whether the application was opened by pressing on a notification (so you
could open a specific screen for example). The API provides two APIs for handling interaction:

- `getInitialNotification`: When the application is opened from a quit state.
- `onNotificationOpenedApp`: When the application is running, but in the background.

These fire for the **default notification open** (user taps the notification body). They do **not** fire for
custom notification-category actions or dismiss; those interactions need a dedicated response API (tracked
internally — do not route custom-action navigation through opened/initial).

To handle both scenarios, the code can be executed during setup. For example, using [React Navigation](https://reactnavigation.org/)
we can set an initial route when the app is opened from a quit state, and push to a new screen when the app is in a background state:

```jsx
import React, { useState, useEffect } from 'react';
import { Linking, ActivityIndicator } from 'react-native';
import {
  getMessaging,
  getInitialNotification,
  onNotificationOpenedApp,
} from '@react-native-firebase/messaging';
import { NavigationContainer, useNavigation } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

const Stack = createStackNavigator();
const NAVIGATION_IDS = ['home', 'post', 'settings'];
const messaging = getMessaging();

function buildDeepLinkFromNotificationData(data): string | null {
  const navigationId = data?.navigationId;
  if (!NAVIGATION_IDS.includes(navigationId)) {
    console.warn('Unverified navigationId', navigationId)
    return null;
  }
  if (navigationId === 'home') {
    return 'myapp://home';
  }
  if (navigationId === 'settings') {
    return 'myapp://settings';
  }
  const postId = data?.postId;
  if (typeof postId === 'string') {
    return `myapp://post/${postId}`
  }
  console.warn('Missing postId')
  return null
}

const linking = {
  prefixes: ['myapp://'],
  config: {
    initialRouteName: 'Home',
    screens: {
      Home: 'home',
      Post: 'post/:id',
      Settings: 'settings'
    }
  },
  async getInitialURL() {
    const url = await Linking.getInitialURL();
    if (typeof url === 'string') {
      return url;
    }
    //getInitialNotification: When the application is opened from a quit state.
    const message = await getInitialNotification(messaging);
    const deeplinkURL = buildDeepLinkFromNotificationData(message?.data);
    if (typeof deeplinkURL === 'string') {
      return deeplinkURL;
    }
  },
  subscribe(listener: (url: string) => void) {
    const onReceiveURL = ({url}: {url: string}) => listener(url);

    // Listen to incoming links from deep linking
    const linkingSubscription = Linking.addEventListener('url', onReceiveURL);

    //onNotificationOpenedApp: When the application is running, but in the background.
    const unsubscribe = onNotificationOpenedApp(messaging, remoteMessage => {
      const url = buildDeepLinkFromNotificationData(remoteMessage.data)
      if (typeof url === 'string') {
        listener(url)
      }
    });

    return () => {
      linkingSubscription.remove();
      unsubscribe();
    };
  },
}

function App() {
  return (
    <NavigationContainer linking={linking} fallback={<ActivityIndicator animating />}>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Post" component={PostScreen} />
        <Stack.Screen name="Settings" component={SettingsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}
```

**Quick Tip:** On `Android` you can test receiving remote notifications on the emulator. On `iOS`,
use a **physical device** for real APNs tokens and delivery. On ARM64 Simulator,
`registerDeviceForRemoteMessages` may reject with `messaging/registration-timeout` (UIKit register
is skipped intentionally so the main thread cannot hang) — see
[Auto Registration (iOS)](/messaging/usage#auto-registration-ios) and
[Migrating to v26 — iOS APNs registration](/migrating-to-v26#ios-apns-registration-arm64-simulator--new-promise-rejections).

# Getting a Device Token

To send messages to a device, you would need the FCM token for it, which you can get using the `getToken(messaging)` method. An example is available on '[Notifee pages](https://notifee.app/react-native/docs/integrations/fcm)'.

On iOS, React Native Firebase Messaging automatically registers the device with APNs by default. You only need to
manually call `registerDeviceForRemoteMessages(messaging)` before `getToken(messaging)` if you disabled
auto-registration with `messaging_ios_auto_register_for_remote_messages` in `firebase.json`, or if you otherwise call
`getToken(messaging)` before the app has registered for remote messages. See
[Auto Registration (iOS)](/messaging/usage#auto-registration-ios) for the manual registration example.

```jsx
import {
  getMessaging,
  registerDeviceForRemoteMessages,
  getToken,
} from '@react-native-firebase/messaging';

const messaging = getMessaging();
// Optional: not needed in most cases, and a no-op if already registered.
await registerDeviceForRemoteMessages(messaging);
const token = await getToken(messaging);
// save the token to the db
```

# Advanced Local Notifications

FCM provides support for displaying basic notifications to users with minimal integration required. If however you require
more advanced notifications we recommend using our separate local notifications package '[Notifee](https://notifee.app)'.

> Notifee is free to use and fully open source.

## Notifee - Android Features

- [Advanced channel and group management](https://notifee.app/react-native/docs/android/channels).
- Custom appearance with [HTML text styling](https://notifee.app/react-native/docs/android/appearance#text-styling), [custom icons](https://notifee.app/react-native/docs/android/appearance#icons), [badge support](https://notifee.app/react-native/docs/android/appearance#badges), [colors](https://notifee.app/react-native/docs/android/appearance#color) and more.
- Behavior management such as [custom sounds](https://notifee.app/react-native/docs/android/behaviour#sound), [vibration patterns](https://notifee.app/react-native/docs/android/behaviour#vibration), device [notification light management](https://notifee.app/react-native/docs/android/behaviour#lights) and more.
- Displaying on-going [Foreground Service Notifications](https://notifee.app/react-native/docs/android/foreground-service) for dealing with long-running background tasks.
- Advanced [interaction handling](https://notifee.app/react-native/docs/android/interaction) with action buttons, quick reply features and more.
- Support for built in styling; [Big Picture Style](https://notifee.app/react-native/docs/android/styles#big-picture), [Big Text Style](https://notifee.app/react-native/docs/android/styles#big-text), [Inbox Style](https://notifee.app/react-native/docs/android/styles#inbox) & [Messaging Style](https://notifee.app/react-native/docs/android/styles#messaging) notifications.
- Adding [Progress Indicators](https://notifee.app/react-native/docs/android/progress-indicators) & [Timers](https://notifee.app/react-native/docs/android/timers) to your notification.

## Notifee - iOS Features

- Advanced [Permission](https://notifee.app/react-native/docs/ios/permissions) management.
- Behavior management such as [custom sounds](https://notifee.app/react-native/docs/ios/behaviour#sound) and [critical notifications](https://notifee.app/react-native/docs/ios/behaviour#critical-notifications).
- Creating [actions & categories](https://notifee.app/react-native/docs/ios/categories).

To learn more about integrating FCM with Notifee, view the [integration](https://notifee.app/react-native/docs/integrations/fcm) documentation.
```

### Server Integration

Source: https://rnfirebase.io/messaging/server-integration

```mdx

The Cloud Messaging module provides the tools required to enable you to send custom messages directly from your own servers.
For example, you could send a FCM message to a specific device when a new chat message is saved to your database
and display a [notification](/messaging/notifications) or update local device storage so the message is instantly available.

Firebase provides a number of SDKs in different languages such as [Node.JS](https://www.npmjs.com/package/firebase-admin),
[Java](https://firebase.google.com/docs/reference/admin/java/reference/com/google/firebase/messaging/package-summary),
[Python](https://firebase.google.com/docs/reference/admin/python/firebase_admin.messaging),
[C#](https://firebase.google.com/docs/reference/admin/dotnet/namespace/firebase-admin/messaging) and
[Go](https://godoc.org/firebase.google.com/go/messaging). It also supports sending messages over
[HTTP](https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages). These methods allow you to send messages
directly to your user's devices via the FCM servers.

## Device tokens

To send a message to a device, you must access its unique token. A token is automatically generated by the device and
can be accessed using the Cloud Messaging module. The token should be saved inside of your systems data-store and should
be easily accessible when required.

The examples below use a [Cloud Firestore](/firestore) database to store and manage the tokens, and [Authentication](/auth)
to manage the users identity. You can however use any datastore or authentication method of your choice.

> If using iOS, ensure you have read and followed the steps in [registered with FCM](/messaging#ios---registering-devices-with-fcm) and [requested user permission](/messaging#ios---requesting-permissions) before trying to receive messages!

## Saving tokens

Once your application has started, you can call the `getToken` method on the Cloud Messaging module to get the unique
device token (if using a different push notification provider, such as Amazon SNS, you will need to call `getAPNSToken` on iOS):

```jsx
import React, { useEffect } from 'react';
import { getMessaging, getToken, onTokenRefresh } from '@react-native-firebase/messaging';
import { getAuth } from '@react-native-firebase/auth';
import { getFirestore, doc, updateDoc, arrayUnion } from '@react-native-firebase/firestore';
import { Platform } from 'react-native';

async function saveTokenToDatabase(token) {
  // Assume user is already signed in
  const userId = getAuth().currentUser.uid;

  // Add the token to the users datastore
  await updateDoc(doc(getFirestore(), 'users', userId), {
    tokens: arrayUnion(token),
  });
}

function App() {
  useEffect(() => {
    const messaging = getMessaging();

    // Get the device token
    getToken(messaging).then(token => {
      return saveTokenToDatabase(token);
    });

    // If using other push notification providers (ie Amazon SNS, etc)
    // you may need to get the APNs token instead for iOS:
    // if(Platform.OS == 'ios') { getAPNSToken(messaging).then(token => { return saveTokenToDatabase(token); }); }

    // Listen to whether the token changes
    return onTokenRefresh(messaging, token => {
      saveTokenToDatabase(token);
    });
  }, []);
}
```

The above code snippet has a single purpose; storing the device FCM token on a remote database. The `useEffect` is fired
when the `App` component runs and immediately gets the token. It also listens to any events when the device automatically refreshes
the token.

Inside of the `saveTokenToDatabase` method, we store the token on a record specifically relating to the current user. You may also
notice that the token is being added via the `arrayUnion` method. A user can have more than one token (for example using 2 devices)
so it's important to ensure that we store all tokens in the database.

## Using tokens

With the tokens stored in a secure datastore, we now have the ability to send messages via FCM to those devices.

> The following example uses the Node.JS `firebase-admin` package to send messages to our devices, however any SDK (listed above)
> can be used.

Go ahead and setup the [`firebase-tools`](https://www.npmjs.com/package/firebase-admin) library on your server environment.
Once setup, our script needs to perform two actions:

1. Fetch the tokens required to send the message.
2. Send a data payload to the devices that the tokens are registered to.

Imagine our application being similar to Instagram. Users are able to upload pictures, and other users can "like" those pictures.
Each time a post is liked, we want to send a message to the user that uploaded the picture. The code below simulates a function
which is called with all of the information required when a picture is liked:

```js
// Node.js
var admin = require('firebase-admin');

// ownerId - who owns the picture someone liked
// userId - id of the user who liked the picture
// picture - metadata about the picture

async function onUserPictureLiked(ownerId, userId, picture) {
  // Get the owners details
  const owner = admin.firestore().collection('users').doc(ownerId).get();

  // Get the users details
  const user = admin.firestore().collection('users').doc(userId).get();

  await admin.messaging().sendEachForMulticast({
    tokens: owner.tokens, // ['token_1', 'token_2', ...]
    data: {
      owner: JSON.stringify(owner),
      user: JSON.stringify(user),
      picture: JSON.stringify(picture),
    },
    apns: {
      payload: {
        aps: {
          // Required for background/quit data-only messages on iOS
          // Note: iOS frequently will receive the message but decline to deliver it to your app.
          //           This is an Apple design choice to favor user battery life over data-only delivery
          //           reliability. It is not under app control, though you may see the behavior in device logs.
          'content-available': true,
          // Required for background/quit data-only messages on Android
          priority: 'high',
        },
      },
    },
  });
}
```

Data-only messages are sent as low priority on both Android and iOS and will not trigger the `setBackgroundMessageHandler`
by default. To enable this functionality, you must set the "priority" to `high` on Android and enable the
`content-available` flag for iOS in the message payload.

> If using the FCM REST API, see the [following documentation](https://firebase.google.com/docs/cloud-messaging/http-server-ref) on setting `priority` and `content-available`!

The `data` property can send an object of key-value pairs totaling `4KB` as string values (hence the `JSON.stringify`).

Back within our application, as explained in the [Usage](/messaging/usage) documentation, our message handlers will receive a
[`RemoteMessage`](https://invertase.github.io/react-native-firebase/_react-native-firebase/messaging/types/messaging/RemoteMessage.html) payload containing the message details sent from the server:

```jsx
import { useEffect } from 'react';
import { getMessaging, onMessage } from '@react-native-firebase/messaging';

function App() {
  useEffect(() => {
    const messaging = getMessaging();
    const unsubscribe = onMessage(messaging, async remoteMessage => {
      const owner = JSON.parse(remoteMessage.data.owner);
      const user = JSON.parse(remoteMessage.data.user);
      const picture = JSON.parse(remoteMessage.data.picture);

      console.log(`The user "${user.name}" liked your picture "${picture.name}"`);
    });

    return unsubscribe;
  }, []);
}
```

Your application code can then handle messages as you see fit; updating local cache, displaying a [notification](/messaging/notifications)
or updating UI. The possibilities are endless!

## Signing out users

Firebase Cloud Messaging tokens are associated with the instance of the installed app. By default, only token expiration or uninstalling/reinstalling the app will generate a fresh token.

This means that by default, if your app has users and you allow them to log out and log in on the same app on the same device, the same FCM token will be used for both users. Usually this is not what you want, so you must take care to cycle the FCM token at the same time you handle user logout/login.

How and when you invalidate a token and generate a new one will be specific to your project, but a common pattern is to delete the FCM token during logout and update your back end to remove it, then to fetch the FCM token during login and update your back end systems to associate the new token with the logged in user.

Use [`deleteToken`](https://invertase.github.io/react-native-firebase/_react-native-firebase/messaging/modular/deleteToken.html) and [`getToken`](https://invertase.github.io/react-native-firebase/_react-native-firebase/messaging/modular/getToken.html) from the modular API.

Note that when a token is deleted by calling the `deleteToken` method, it is immediately and permanently invalid.

## Send messages to topics

When devices [subscribe to topics](/messaging/usage#topics), you can send messages without specifying/storing any device
tokens.

Using the `firebase-admin` Admin SDK as an example, we can send a message to devices subscribed to a topic:

```js
const admin = require('firebase-admin');

const message = {
  data: {
    type: 'warning',
    content: 'A new weather warning has been created!',
  },
  topic: 'weather',
};

admin
  .messaging()
  .send(message)
  .then(response => {
    console.log('Successfully sent message:', response);
  })
  .catch(error => {
    console.log('Error sending message:', error);
  });
```

## Conditional topics

To send a message to a combination of topics, specify a condition, which is a boolean expression that specifies the target
topics. For example, the following condition will send messages to devices that are subscribed to `weather` and either `news`
or `traffic`:

```json
condition: "'weather' in topics && ('news' in topics || 'traffic' in topics)"
```

To send a message to this condition, replace the `topic` key with `condition`:

```js
const admin = require('firebase-admin');

const message = {
  data: {
    content: 'New updates are available!',
  },
  condition: "'weather' in topics && ('news' in topics || 'traffic' in topics)",
};

admin
  .messaging()
  .send(message)
  .then(response => {
    console.log('Successfully sent message:', response);
  })
  .catch(error => {
    console.log('Error sending message:', error);
  });
```

## Send messages with image

Both the Notifications composer and the FCM API support image links in the message payload.

### iOS

To successfully send an image using the Admin SDK it's important that the `ApnsConfig` options are set:

```js
const payload = {
  notification: {
    body: 'This is an FCM notification that displays an image!',
    title: 'FCM Notification',
  },
  apns: {
    payload: {
      aps: {
        'mutable-content': 1, // 1 or true
      },
    },
    fcmOptions: {
      imageUrl: 'image-url',
    },
  },
};
```

> Check out the [official Firebase documentation](https://firebase.google.com/docs/cloud-messaging/ios/send-image) to see the list of available configuration for iOS.

### Android

Similarly to iOS, some configurations specific to Android are needed:

```js
const payload = {
  notification: {
    body: 'This is an FCM notification that displays an image!',
    title: 'FCM Notification',
  },
  android: {
    notification: {
      imageUrl: 'image-url',
    },
  },
};
```

> If you want to know more about sending an image on Android have a look at [the documentation](https://firebase.google.com/docs/cloud-messaging/android/send-image).

## Pulling it all together

It's possible to send one notification that will be delivered to both platforms using the Admin SDK:

```js
const admin = require('firebase-admin');

// Create a list containing up to 500 registration tokens.
// These registration tokens come from the client FCM SDKs.
const registrationTokens = ['YOUR_REGISTRATION_TOKEN_1', 'YOUR_REGISTRATION_TOKEN_2'];

const message = {
  tokens: registrationTokens,
  notification: {
    body: 'This is an FCM notification that displays an image!',
    title: 'FCM Notification',
  },
  apns: {
    payload: {
      aps: {
        'mutable-content': 1,
      },
    },
    fcmOptions: {
      imageUrl: 'image-url',
    },
  },
  android: {
    notification: {
      imageUrl: 'image-url',
    },
  },
};

admin
  .messaging()
  .sendEachForMulticast(message)
  .then(response => {
    console.log('Successfully sent message:', response);
  })
  .catch(error => {
    console.log('Error sending message:', error);
  });
```

If you want to read more about building send requests with the Admin SDK check out [this link](https://firebase.google.com/docs/cloud-messaging/send/v1-api).
```

### Cloud Messaging

Source: https://rnfirebase.io/messaging/usage

```mdx

# Installation

This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app" module, view the
[Getting Started](/) documentation.

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the messaging module
yarn add @react-native-firebase/messaging

# If you're developing your app using iOS, run this command
cd ios/ && pod install
```

> iOS requires further configuration before you can start receiving and sending
> messages through Firebase. Read the documentation on how to [setup iOS with Firebase Cloud Messaging](/messaging/usage/ios-setup).

> Use of the `sendMessage()` API and it's associated listeners requires a custom `XMPP` server. Read the documentation on how to [Messaging with XMPP](/messaging/usage/messaging-with-xmpp).

If you're using an older version of React Native without auto-linking support, or wish to integrate into an existing project,
you can follow the manual installation steps for [iOS](/messaging/usage/installation/ios) and [Android](/messaging/usage/installation/android).

# Expo

## iOS - Notifications entitlement

Since Expo SDK51, Notifications entitlement is no longer always added to iOS projects during prebuild. If your project uses push notifications, you may need to add the aps-environment entitlement to your app config:

```json
{
  "expo": {
    "ios": {
      "entitlements": {
        "aps-environment": “production”
      }
    }
  }
}
```

## iOS - Remote notification

If you require `remote notification` on Expo, you can also add this to your Expo `app.json` or `app.config.js`

```json
{
  "expo": {
    "ios": {
      "infoPlist": {
        "UIBackgroundModes": ["remote-notification"]
      }
    }
  }
}
```

## Android - Google Play Notification Delegation

If you use the REST v1 APIs (used by the Firebase admin SDKs) and your app is running on Android Q+ with current Google Play services, Google implemented "Notification Delegation" for messages. Notification delegation is not currently compatible with react-native-firebase. Specifically, if your notifications are delegated via proxy to Play Services, then your messaging listeners will not be called.

To work around this incompatibility, react-native-firebase disables notification delegation by default currently, using the `AndroidManifest.xml` method listed as one of the options described here: https://firebase.google.com/docs/cloud-messaging/android/message-priority#proxy.

You may re-enable notification delegation if your use case requires it and you can accept the messaging listener methods not executing for delegated messages by altering the firebase.json setting `messaging_android_notification_delegation_enabled` to `true`.

You may also use the new messaging APIs to get and set the notification delegation state for the app, as desired.

# Platform support and New Architecture

|                      |                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| **Platforms**        | Android, iOS (native Firebase SDK)                                                             |
| **New Architecture** | **Required** from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement). |

**Platform notes:** `getAPNSToken` / `setAPNSToken` are **iOS only**. FCM foreground/background listeners use the legacy native event proxy (same runtime behavior as pre-v26). See [Migrating to v26 — Platform behavior differences](/migrating-to-v26#platform-behavior-differences).

# What does it do

React Native Firebase provides native integration of Firebase Cloud Messaging (FCM) for both Android & iOS. FCM is a cost
free service, allowing for server-device and device-device communication. The React Native Firebase Messaging module provides
a simple JavaScript API to interact with FCM.

<YouTube id="sioEY4tWmLI" />

The module also provides basic support for displaying local notifications, to learn more view the [Notifications](/messaging/notifications) documentation.

# Usage

## iOS - Requesting permissions

> **Deprecated:** `requestPermission`, `hasPermission`, and `AuthorizationStatus` are deprecated. Use [react-native-permissions](https://github.com/zoontek/react-native-permissions) or [expo-notifications](https://docs.expo.dev/versions/latest/sdk/notifications/) instead. See [issue #6283](https://github.com/invertase/react-native-firebase/issues/6283).

iOS prevents messages containing notification (or 'alert') payloads from being displayed unless you have received explicit permission from the user.

> To learn more about local notifications, view the [Notifications](/messaging/notifications) documentation.

This module provides a `requestPermission` method which triggers a native permission dialog requesting the user's permission:

```js
import {
  getMessaging,
  requestPermission,
  AuthorizationStatus,
} from '@react-native-firebase/messaging';

async function requestUserPermission() {
  const messaging = getMessaging();
  const authStatus = await requestPermission(messaging);
  const enabled =
    authStatus === AuthorizationStatus.AUTHORIZED || authStatus === AuthorizationStatus.PROVISIONAL;

  if (enabled) {
    console.log('Authorization status:', authStatus);
  }
}
```

The permissions API for iOS provides much more fine-grain control over permissions and how they're handled within your
application. To learn more, view the advanced [iOS Permissions](/messaging/ios-permissions) documentation.

## Android - Requesting permissions

> **Deprecated:** `requestPermission` and `hasPermission` are deprecated no-ops on Android API level 32 and below. Use [react-native-permissions](https://github.com/zoontek/react-native-permissions) or [expo-notifications](https://docs.expo.dev/versions/latest/sdk/notifications/) for notification permission requests on API level 33+. See [issue #6283](https://github.com/invertase/react-native-firebase/issues/6283).

On Android API level 32 and below, you do not need to request user permission. This method can still be called on Android devices; however, and will always resolve successfully. For API level 33+ you will need to request the permission manually using either the built-in react-native `PermissionsAndroid` APIs or a related module such as `react-native-permissions` or `expo-notifications`

```
  import {PermissionsAndroid} from 'react-native';
  PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
```

## Receiving messages

FCM messages can be sent to _real_ Android/iOS devices and Android emulators (iOS simulators however do _not_ handle cloud messages) via a number of methods (see below).
A message is simply a payload of data which can be used however you see fit within your application.

Common use-cases for handling messages could be:

- Displaying a notification (see [Notifications](/messaging/notifications)).
- Syncing message data silently on the device (e.g. via `AsyncStorage`).
- Updating the application's UI.

> To learn about how to send messages to devices from your own server setup, view the
> [Server Integration](/messaging/server-integration) documentation.

Depending on the devices state, incoming messages are handled differently by the device and module. To understand these
scenarios, it is first important to establish the various states a device can be in:

| State          | Description                                                                                                                                                                                               |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Foreground** | When the application is open and in view.                                                                                                                                                                 |
| **Background** | When the application is open, however in the background (minimized). This typically occurs when the user has pressed the "home" button on the device or has switched to another app via the app switcher. |
| **Quit**       | When the device is locked or application is not active or running. The user can quit an app by "swiping it away" via the app switcher UI on the device.                                                   |

The user must have opened the app before messages can be received. If the user force quits the app from the device settings, it must be re-opened again before receiving messages.

Depending on the contents of the message, it's important to understand both how the device will handle the message (e.g. display a notification, or even ignore it) and also how the library sends events to your own listeners.

### Message handlers

The device state and message contents determines which handler will be called:

|                         | Foreground  | Background                                      | Quit                                            |
| ----------------------- | ----------- | ----------------------------------------------- | ----------------------------------------------- |
| **Notification**        | `onMessage` | `setBackgroundMessageHandler`                   | `setBackgroundMessageHandler`                   |
| **Notification + Data** | `onMessage` | `setBackgroundMessageHandler`                   | `setBackgroundMessageHandler`                   |
| **Data**                | `onMessage` | `setBackgroundMessageHandler` (**_see below_**) | `setBackgroundMessageHandler` (**_see below_**) |

- In cases where the message is data-only and the device is in the background or quit, both Android & iOS treat the message
  as low priority and will ignore it (i.e. no event will be sent). You can however increase the priority by setting the `priority` to `high` (Android) and
  `content-available` to `true` (iOS) properties on the payload.

- On iOS in cases where the message is data-only and the device is in the background or quit, the message will be delayed
  until the background message handler is registered via setBackgroundMessageHandler, signaling the application's javascript
  is loaded and ready to run.

To learn more about how to send these options in your message payload, view the Firebase documentation for your [FCM API implementation](https://firebase.google.com/docs/cloud-messaging/customize-messages/setting-message-priority).

### Notifications

The device state and message contents can also determine whether a [Notification](/messaging/notifications) will be displayed:

|                         | Foreground             | Background             | Quit                   |
| ----------------------- | ---------------------- | ---------------------- | ---------------------- |
| **Notification**        | Notification: &#10060; | Notification: &#9989;  | Notification: &#9989;  |
| **Notification + Data** | Notification: &#10060; | Notification: &#9989;  | Notification: &#9989;  |
| **Data**                | Notification: &#10060; | Notification: &#10060; | Notification: &#10060; |

### Foreground state messages

To listen to messages in the foreground, call the `onMessage` method inside of your application code. Code
executed via this handler has access to React context and is able to interact with your application (e.g. updating the state or UI).

For example, the React Native [`Alert`](https://reactnative.dev/docs/alert) API could be used to display a new Alert
each time a message is delivered'

```js
import React, { useEffect } from 'react';
import { Alert } from 'react-native';
import { getMessaging, onMessage } from '@react-native-firebase/messaging';

function App() {
  useEffect(() => {
    const messaging = getMessaging();
    const unsubscribe = onMessage(messaging, async remoteMessage => {
      Alert.alert('A new FCM message arrived!', JSON.stringify(remoteMessage));
    });

    return unsubscribe;
  }, []);
}
```

The `remoteMessage` property contains all of the information about the message sent to the device from FCM, including
any custom data (via the `data` property) and notification data. To learn more, view the [`RemoteMessage`](https://invertase.github.io/react-native-firebase/_react-native-firebase/messaging/types/messaging/RemoteMessage.html)
API reference.

If the `RemoteMessage` payload contains a `notification` property when sent to the `onMessage` handler, the device
will not show any notification to the user. Instead, you could trigger a [local notification](/messaging/notifications#notifee---advanced-notifications)
or update the in-app UI to signal a new notification.

### Background & Quit state messages

> Note: If you use @notifee/react-native, since v7.0.0, `onNotificationOpenedApp` and `getInitialNotification` will no longer trigger as notifee will handle the event.

When the application is in a background or quit state, the `onMessage` handler will not be called when receiving messages.
Instead, you need to setup a background callback handler via the `setBackgroundMessageHandler` method.

To setup a background handler, call the `setBackgroundMessageHandler` outside of your application logic as early as possible:

```jsx
// index.js
import { AppRegistry } from 'react-native';
import { getMessaging, setBackgroundMessageHandler } from '@react-native-firebase/messaging';
import App from './App';

const messaging = getMessaging();

// Register background handler
setBackgroundMessageHandler(messaging, async remoteMessage => {
  console.log('Message handled in the background!', remoteMessage);
});

AppRegistry.registerComponent('app', () => App);
```

The handler must return a promise once your logic has completed to free up device resources. It must not attempt to update
any UI (e.g. via state) - you can however perform network requests, update local storage etc.

The `remoteMessage` property contains all of the information about the message sent to the device from FCM, including
any custom data via the `data` property. To learn more, view the [`RemoteMessage`](https://invertase.github.io/react-native-firebase/_react-native-firebase/messaging/types/messaging/RemoteMessage.html)
API reference.

If the `RemoteMessage` payload contains a `notification` property when sent to the `setBackgroundMessageHandler` handler, the device
will have displayed a [notification](/messaging/notifications) to the user.

#### Data-only messages

When an incoming message is "data-only" (contains no `notification` option), both Android & iOS regard it as low priority
and will prevent the application from waking (ignoring the message). To allow data-only messages to trigger the background
handler, you must set the "priority" to "high" on Android, and enable the `content-available` flag on iOS. For example,
if using the Node.js [`firebase-admin`](https://www.npmjs.com/package/firebase-admin) package to send a message:

```js
admin.messaging().sendToDevice(
  [], // device fcm tokens...
  {
    data: {
      owner: JSON.stringify(owner),
      user: JSON.stringify(user),
      picture: JSON.stringify(picture),
    },
  },
  {
    // Required for background/quit data-only messages on iOS
    contentAvailable: true,
    // Required for background/quit data-only messages on Android
    priority: 'high',
  },
);
```

For iOS specific "data-only" messages, the message must include the appropriate APNs headers as well as the `content-available` flag in order to trigger the background handler. For example, if using the Node.js [`firebase-admin`](https://www.npmjs.com/package/firebase-admin) package to send a "data-only" message to an iOS device:

```js
admin.messaging().send({
  data: {
    //some data
  },
  apns: {
    payload: {
      aps: {
        contentAvailable: true,
      },
    },
    headers: {
      'apns-push-type': 'background',
      'apns-priority': '5',
      'apns-topic': '', // your app bundle identifier
    },
  },
  //must include token, topic, or condition
  //token: //device token
  //topic: //notification topic
  //condition: //notification condition
});
```

View the [Sending Notification Requests to APNs](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns/) documentation to learn more about APNs headers.

These options can be applied to all FCM messages. View the [Server Integration](/messaging/server-integration) documentation
to learn more about other available SDKs.

#### Background Application State

Although the library supports handling messages in background/quit states, the underlying implementation on how this works is different on Android & iOS.

On Android, a [Headless JS](https://reactnative.dev/docs/headless-js-android) task (an Android only feature) is created that runs separately to your main React component; allowing your background handler code to run without mounting your root component.

On iOS however, when a message is received the device silently starts your application in a background state. At this point, your background handler (via `setBackgroundMessageHandler`) is triggered, but your root React component also gets mounted. This can be problematic for some users since any side-effects will be called inside of your app (e.g. `useEffects`, analytics events/triggers etc). To get around this problem,
you can configure your `AppDelegate.m` file (see instructions below) to inject a `isHeadless` prop into your root component. Use this property to conditionally render `null` ("nothing") if your app is launched in the background:

```jsx
// index.js
import { AppRegistry } from 'react-native';
import { getMessaging, setBackgroundMessageHandler } from '@react-native-firebase/messaging';

const messaging = getMessaging();

// Handle background messages using setBackgroundMessageHandler
setBackgroundMessageHandler(messaging, async remoteMessage => {
  console.log('Message handled in the background!', remoteMessage);
});

// Check if app was launched in the background and conditionally render null if so
function HeadlessCheck({ isHeadless }) {
  if (isHeadless) {
    // App has been launched in the background by iOS, ignore
    return null;
  }

  // Render the app component on foreground launch
  return <App />;
}

// Your main application component defined here
function App() {
  // Your application
}

AppRegistry.registerComponent('app', () => HeadlessCheck);
```

To inject a `isHeadless` prop into your app, please update your `AppDelegate.m` file as instructed below:

```objectivec
// add this import statement at the top of your `AppDelegate.m` file
#import "RNFBMessagingModule.h"

// in "(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions" method
// Use `addCustomPropsToUserProps` to pass in props for initialization of your app
// Or pass in `nil` if you have none as per below example
// For `withLaunchOptions` please pass in `launchOptions` object
// and use it to set `self.initialProps` (available with react-native >= 0.71.1, older versions need a more difficult style, upgrading is recommended)

self.initialProps = [RNFBMessagingModule addCustomPropsToUserProps:nil withLaunchOptions:launchOptions];
```

- For projects that use react-native-navigation (or if you just don't want to mess with your launchProperties) you can use the `getIsHeadless` method (iOS only) from messaging like so:

```jsx
import { getMessaging, getIsHeadless } from '@react-native-firebase/messaging';

const messaging = getMessaging();

getIsHeadless(messaging).then(isHeadless => {
  // do sth with isHeadless
});
```

On Android, the `isHeadless` prop will not exist.

#### iOS Background Limitation

On iOS devices, the user is able to toggle Background App Refresh in device's Settings. Furthermore, the Background App Refresh setting will automatically be off if the device is in low power mode.

If the iOS Background App Refresh mode is off, your handler configured in `setBackgroundMessageHandler` will not be triggered.

### Topics

Topics are a mechanism which allow a device to subscribe and unsubscribe from named PubSub channels, all managed via FCM.
Rather than sending a message to a specific device by FCM token, you can instead send a message to a topic and any
devices subscribed to that topic will receive the message.

Topics allow you to simplify FCM [server integration](/messaging/server-integration) as you do not need to keep a store of
device tokens. There are however some things to keep in mind about topics:

- Messages sent to topics should not contain sensitive or private information. Do not create a topic for a specific user
  to subscribe to.
- Topic messaging supports unlimited subscriptions for each topic.
- One app instance can be subscribed to no more than 2000 topics.
- The frequency of new subscriptions is rate-limited per project. If you send too many subscription requests in a short
  period of time, FCM servers will respond with a 429 RESOURCE_EXHAUSTED ("quota exceeded") response. Retry with exponential backoff.
- A server integration can send a single message to multiple topics at once. This however is limited to 5 topics.

To learn more about how to send messages to devices subscribed to topics, view the [Send messages to topics](/messaging/server-integration#send-messages-to-topics)
documentation.

#### Subscribing to topics

To subscribe a device, call the `subscribeToTopic` method with the topic name (must not include "/"):

```js
import { getMessaging, subscribeToTopic } from '@react-native-firebase/messaging';

const messaging = getMessaging();

subscribeToTopic(messaging, 'weather').then(() => console.log('Subscribed to topic!'));
```

#### Unsubscribing to topics

To unsubscribe from a topic, call the `unsubscribeFromTopic` method with the topic name:

```js
import { getMessaging, unsubscribeFromTopic } from '@react-native-firebase/messaging';

const messaging = getMessaging();

unsubscribeFromTopic(messaging, 'weather').then(() => console.log('Unsubscribed fom the topic!'));
```

# firebase.json

Messaging can be further configured to provide more control over how FCM is handled internally within your application.

## Auto Registration (iOS)

React Native Firebase Messaging automatically registers the device with APNs to receive remote messages. If you need
to manually control registration you can disable this via the `firebase.json` file:

```json
// <projectRoot>/firebase.json
{
  "react-native": {
    "messaging_ios_auto_register_for_remote_messages": false
  }
}
```

Once auto-registration is disabled you must manually call `registerDeviceForRemoteMessages` in your JavaScript code as
early as possible in your application startup;

```js
import { getMessaging, registerDeviceForRemoteMessages } from '@react-native-firebase/messaging';

async function registerAppWithFCM() {
  await registerDeviceForRemoteMessages(getMessaging());
}
```

> **ARM64 iOS Simulator:** React Native Firebase intentionally does **not** call UIKit
> `registerForRemoteNotifications` on Apple Silicon Simulator builds (registration,
> auto-registration, and `requestPermission`'s APNs side-effect). Calling that API can block the
> main thread indefinitely. `registerDeviceForRemoteMessages` therefore stays pending until a
> ~10 second **global-queue** timer rejects with `messaging/registration-timeout`.
> `requestPermission` still resolves with `AuthorizationStatus` (it does **not** reject with that
> code). You will not get a real APNs device token on that simulator — use a physical iOS device
> when you need end-to-end push registration. If `registerDeviceForRemoteMessages` is called again
> before a prior attempt settles, the earlier promise rejects with
> `messaging/registration-superseded`. See
> [Migrating to v26 — iOS APNs registration](/migrating-to-v26#ios-apns-registration-arm64-simulator--new-promise-rejections).

## Foreground Presentation Options (iOS)

React Native Firebase Messaging configures how to present a notification in a foreground app.
Refer to [UNNotificationPresentationOptions](https://developer.apple.com/documentation/usernotifications/unnotificationpresentationoptions) for the details.

```json
// <projectRoot>/firebase.json
{
  "react-native": {
    "messaging_ios_foreground_presentation_options": ["badge", "sound", "list", "banner"]
  }
}
```

## Auto initialization

Firebase generates an Instance ID, which FCM uses to generate a registration token and which Analytics uses for data collection.
When an Instance ID is generated, the library will upload the identifier and configuration data to Firebase. In most cases,
you do not need to change this behavior.

If you prefer to prevent Instance ID auto-generation, disable auto initialization for FCM and Analytics:

```json
// <projectRoot>/firebase.json
{
  "react-native": {
    "analytics_auto_collection_enabled": false,
    "messaging_auto_init_enabled": false
  }
}
```

To re-enable initialization (e.g. once requested permission) call the `setAutoInitEnabled(messaging, true)` method.

## Background handler timeout (Android)

On Android, a background event sent to `setBackgroundMessageHandler` has 60 seconds to resolve before it is automatically
canceled to free up device resources. If you wish to override this value, set the number of milliseconds in your config:

```json
// <projectRoot>/firebase.json
{
  "react-native": {
    "messaging_android_headless_task_timeout": 30000
  }
}
```

## Notification Channel ID

On Android, any message which displays a [Notification](/messaging/notifications) use a default Notification Channel
(created by FCM called "Miscellaneous"). This channel contains basic notification settings which may not be appropriate for
your application. You can change what Channel is used by updating the `messaging_android_notification_channel_id` property:

```json
// <projectRoot>/firebase.json
{
  "react-native": {
    "messaging_android_notification_channel_id": "high-priority"
  }
}
```

Creating and managing Channels is outside of the scope of the React Native Firebase library, however external libraries
such as [Notifee](https://notifee.app/react-native/docs/android/channels) can provide such functionality.

## Notification Color

On Android, any messages which display a [Notification](/messaging/notifications) do not use a color to tint the content
(such as the small icon, title etc). To provide a custom tint color, update the `messaging_android_notification_color` property
with a Android color resource name.

The library provides a set of [predefined colors](https://github.com/invertase/react-native-firebase/blob/main/packages/messaging/android/src/main/res/values/colors.xml) corresponding to the [HTML colors](https://www.w3schools.com/colors/colors_names.asp) for convenience, for example:

```json
// <projectRoot>/firebase.json
{
  "react-native": {
    "messaging_android_notification_color": "@color/hotpink"
  }
}
```

Note that only predefined colors can be used in `firebase.json`. If you want to use a custom color defined in your application resources, then you should set it in the `AndroidManifest.xml` instead.

```xml
<!-- <projectRoot>/android/app/src/main/res/values/colors.xml -->
<resources>
  <color name="my_custom_color">#123456</color>
</resources>

<!-- <projectRoot>/android/app/src/main/AndroidManifest.xml -->

<!--  add "tools" to manifest tag  -->
<manifest xmlns:tools="http://schemas.android.com/tools">
  <application>
      <!-- ... -->

      <meta-data
            android:name="com.google.firebase.messaging.default_notification_color"
            android:resource="@color/my_custom_color"
            tools:replace="android:resource" />
  </application>
</manifest>
```
```

### ML

Source: https://rnfirebase.io/ml/usage

```mdx

# Installation

This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app" module, view the
[Getting Started](/) documentation.

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the ml module
yarn add @react-native-firebase/ml

# If you're developing your app using iOS, run this command
cd ios/ && pod install
```

> **This package is mostly discontinued** since these APIs are no longer available in the latest Firebase SDKs.
> To call the Cloud Vision API from your app the recommended approach is using Firebase
> Authentication and Functions, which gives you a managed, serverless gateway to Google Cloud Vision APIs. For an example
> Functions project see the [vision-annotate-images](https://github.com/firebase/functions-samples/tree/3515b7f38a3c598cdb20152a263372e81719ecda/vision-annotate-images) sample project.

If you're using an older version of React Native without autolinking support, or wish to integrate into an existing project,
you can follow the manual installation steps for [iOS](/ml/usage/installation/ios) and [Android](/ml/usage/installation/android).

# Platform support and New Architecture

|                      |                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| **Platforms**        | Android, iOS (native Firebase SDK)                                                             |
| **New Architecture** | **Required** from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement). |

> **React Native only:** There is no firebase-js-sdk web equivalent for the legacy Firebase ML custom-model API.

# What does it do

All Firebase ML services are cloud-based, with on-device APIs handled by the new, separate [Google MLKit](https://developers.google.com/ml-kit/) (Usable in react-native
as a set of [react-native-mlkit modules](https://www.npmjs.com/org/react-native-mlkit))

Firebase has introduced a custom model downloading API, but the module does not have support for it yet. PRs always welcome!

## Support table

The table below outlines the current module support for each available service, and their support status here

| API                      | Status |
| ------------------------ | ------ |
| Custom Model Downloading | ❌     |

# Usage

Current no public APIs
```

### Performance Monitoring with Axios

Source: https://rnfirebase.io/perf/axios-integration

```mdx

# Axios

The [Axios library](https://github.com/axios/axios) allows you to easily send HTTP requests via your
React Native application. Axios provides functionality allowing all requests & responses to be intercepted, exposing
metadata which can be hooked into the Performance Monitoring library.

## Request Interceptor

Before HTTP requests are sent out of the React Native environment, a callback can be attached to the `request`
property on the axios instance. At this point, a new HTTP metric can be defined on the Performance Monitoring library:

```js
import axios from 'axios';
import { getPerformance, httpMetric } from '@react-native-firebase/perf';

axios.interceptors.request.use(async function (config) {
  try {
    const httpMetricInstance = httpMetric(
      getPerformance(),
      config.url,
      config.method.toUpperCase(),
    );
    config.metadata = { httpMetric: httpMetricInstance };

    // add any extra metric attributes, if required
    // httpMetricInstance.putAttribute('userId', '12345678');

    httpMetricInstance.start();
  } finally {
    return config;
  }
});
```

This callback attaches the HTTP metric returned onto the request metadata, which can later be used on an
incoming response.

## Response Interceptor

Similar to the request interceptor, we can also hook into all responses from the HTTP calls. The response
interceptor can accept two callbacks, one for successful responses and one requests which failed:

```js
import axios from 'axios';

axios.interceptors.response.use(
  async function (response) {
    try {
      // Request was successful, e.g. HTTP code 200

      const { httpMetric } = response.config.metadata;

      // add any extra metric attributes if needed
      // httpMetric.putAttribute('userId', '12345678');

      httpMetric.setHttpResponseCode(response.status);
      httpMetric.setResponseContentType(response.headers['content-type']);
      httpMetric.stop();
    } finally {
      return response;
    }
  },
  async function (error) {
    try {
      // Request failed, e.g. HTTP code 500

      const { httpMetric } = error.config.metadata;

      // add any extra metric attributes if needed
      // httpMetric.putAttribute('userId', '12345678');

      httpMetric.setHttpResponseCode(error.response.status);
      httpMetric.setResponseContentType(error.response.headers['content-type']);
      httpMetric.stop();
    } finally {
      // Ensure failed requests throw after interception
      return Promise.reject(error);
    }
  },
);
```

All outbound requests sent from Axios will appear in your Firebase console, detailing information such as
how long the request took, response codes & more. Such data can give you greater insight into the performance
of your application via any external APIs you may be using.
```

### Performance Monitoring with KY

Source: https://rnfirebase.io/perf/ky-integration

```mdx

# KY

The [KY library](https://github.com/sindresorhus/ky) is a tiny wrapper over fetch providing a simpler API and some useful shortcuts.
KY provides functionality allowing all requests & responses to be intercepted, exposing
metadata which can be passed onto the Performance Monitoring library.

## Before Request Hook

Before HTTP requests are sent out of the React Native environment, a number of functions can be called to modify or do things based on the current request.
We can use this `beforeRequest` hook to create our HTTP metric.

```js
import { getPerformance, httpMetric } from '@react-native-firebase/perf';
import ky from 'ky';

const getContentLength = headers => {
  const length = getHeader('Content-Length', headers);
  return length ? Number(length) : null;
};

const getHeader = (header, headers) => headers.get(header) || headers.get(header.toLowerCase());

const api = ky.create({
  hooks: {
    beforeRequest: [
      async (request, options) => {
        const { url, method } = request;
        options.metric = httpMetric(getPerformance(), url, method);

        // add any extra metric attributes, if required
        // options.metric.putAttribute('userId', '12345678');
        // options.metric.setRequestPayloadSize(1234)

        options.metric.start();
      },
    ],
  },
});

export default api;
```

This callback attaches the HTTP metric returned onto the request metadata, which can later be used on an
incoming response.

## After Request Hook

Similar to the before request hook, we can also hook into all responses from HTTP calls.

```js
import ky from 'ky';

const getContentLength = headers => {
  const length = getHeader('Content-Length', headers);
  return length ? Number(length) : null;
};

const getHeader = (header, headers) => headers.get(header) || headers.get(header.toLowerCase());

const api = ky.create({
  hooks: {
    afterResponse: [
      async (request, options, response) => {
        const { status, headers } = response;
        const { metric } = options;
        metric.setHttpResponseCode(status);
        metric.setResponseContentType(getHeader('Content-Type', headers));
        metric.setResponsePayloadSize(getContentLength(headers));
        metric.stop();
      },
    ],
  },
});

export default api;
```

All outbound requests sent from KY will appear in your Firebase console, detailing information such as
how long the request took, response codes & more. Such data can give you greater insight into the performance of your application via any external APIs you may be using.

## Full Example

A working example is provided below. Please adjust and extend according to your use case.

```js
import { getPerformance, httpMetric } from '@react-native-firebase/perf';
import ky from 'ky';

const getContentLength = headers => {
  const length = getHeader('Content-Length', headers);
  return length ? Number(length) : null;
};

const getHeader = (header, headers) => headers.get(header) || headers.get(header.toLowerCase());

const api = ky.create({
  // prevent circular dependency warning
  fetch: fetch,
  hooks: {
    beforeRequest: [
      async (request, options) => {
        const { url, method } = request;
        options.metric = httpMetric(getPerformance(), url, method);

        // add any extra metric attributes, if required
        // options.metric.putAttribute('userId', '12345678');
        // options.metric.setRequestPayloadSize(1234)

        options.metric.start();
      },
    ],

    afterResponse: [
      async (request, options, response) => {
        const { status, headers } = response;
        const { metric } = options;
        metric.setHttpResponseCode(status);
        metric.setResponseContentType(getHeader('Content-Type', headers));
        metric.setResponsePayloadSize(getContentLength(headers));
        metric.stop();
      },
    ],
  },
});

export default api;
```
```

### Performance Monitoring

Source: https://rnfirebase.io/perf/usage

```mdx

# Installation

This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app" module, view the
[Getting Started](/) documentation.

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the performance monitoring module
yarn add @react-native-firebase/perf

# If you're developing your app using iOS, run this command
cd ios/ && pod install
```

If you're using an older version of React Native without autolinking support, or wish to integrate into an existing project,
you can follow the manual installation steps for [iOS](/perf/usage/installation/ios) and [Android](/perf/usage/installation/android).

## Add the Performance Monitoring Plugin

> If you're using Expo, make sure to add the `@react-native-firebase/perf` config plugin to your `app.json` or `app.config.js`. It handles the below installation steps for you. For instructions on how to do that, view the [Expo](/#expo) installation section.

On Android, you need to install the Google Performance Monitoring Plugin which enables automatic
HTTPS network request monitoring.

Add the plugin to your `/android/build.gradle` file as a dependency:

```groovy
buildscript {
    dependencies {
        // ...
        classpath 'com.google.firebase:perf-plugin:2.0.2'
    }
```

Apply the plugin via the `/android/app/build.gradle` file (at the top):

```groovy
apply plugin: 'com.android.application'
apply plugin: 'com.google.firebase.firebase-perf'
```

# Platform support and New Architecture

|                      |                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| **Platforms**        | Android, iOS (native Firebase SDK)                                                             |
| **New Architecture** | **Required** from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement). |

**Platform notes:** `initializePerformance` returns synchronously on RN. Trace, HTTP metric, and screen trace `start`/`stop` are synchronous through TurboModules, matching firebase-js-sdk.

# What does it do

Performance Monitoring allows you to gain insight into key performance characteristics within your React Native application.
It provides a simple API to track custom trace and HTTP request metrics.

<YouTube id="0EHSPFvH7vk" />

Review and analyze that data in the Firebase console. Performance Monitoring helps you to understand where and when the
performance of your app can be improved so that you can use that information to fix performance issues.

Performance Monitoring package automatically traces events and metrics which are sent to Firebase. For more information
on the automatic traces, please see the Firebase Performance Monitoring [documentation](https://firebase.google.com/docs/perf-mon/auto_duration-traces-metrics_ios-android).
The package also allows you to performance monitor custom aspects to your application like network requests & task specific
app code. All performance metrics are available on your Firebase [console](https://console.firebase.google.com/u/0/) performance tab.

# Usage

## Custom tracing

Below is how you would measure the amount of time it would take to complete a specific task in your app code.

```jsx
import { getPerformance, trace } from '@react-native-firebase/perf';

function customTrace() {
  const perf = getPerformance();
  const t = trace(perf, 'custom_trace');

  t.start();
  t.putAttribute('user', 'abcd');
  t.putMetric('credits', 30);
  t.stop();
}
```

## Custom screen traces

Record a custom screen rendering trace (slow frames / frozen frames)

```jsx
import { getPerformance, startScreenTrace } from '@react-native-firebase/perf';

function screenTrace() {
  try {
    const screenTrace = startScreenTrace(getPerformance(), 'FooScreen');
    screenTrace.stop();
  } catch (e) {
    // rejects if iOS or (Android == 8 || Android == 8.1)
    // or if hardware acceleration is off
  }
}
```

## HTTP Request Tracing

Below illustrates you would measure the latency of a HTTP request.

```jsx
import { getPerformance, httpMetric } from '@react-native-firebase/perf';

async function getRequest(url) {
  const metric = httpMetric(getPerformance(), url, 'GET');

  metric.putAttribute('user', 'abcd');

  metric.start();

  const response = await fetch(url);
  metric.setHttpResponseCode(response.status);
  metric.setResponseContentType(response.headers.get('Content-Type'));
  metric.setResponsePayloadSize(response.headers.get('Content-Length'));

  metric.stop();

  return response.json();
}

getRequest('https://api.com').then(json => {
  console.log(json);
});
```

# firebase.json

## Disable Auto-Initialization

The Performance Monitoring module will automatically start collecting data once it is installed. To disable this behavior,
set the `perf_auto_collection_enabled` flag to `false`:

```json
// <project-root>/firebase.json
{
  "react-native": {
    "perf_auto_collection_enabled": false
  }
}
```

To re-enable collection (e.g. once you have the users consent), call the `setPerformanceCollectionEnabled` method:

```js
import { getPerformance } from '@react-native-firebase/perf';
// ...
getPerformance().dataCollectionEnabled = true;
```
```

### Phone Number Verification

Source: https://rnfirebase.io/phone-number-verification/usage

```mdx

# Installation

This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app" module, view the
[Getting Started](/) documentation.

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the phone-number-verification module
yarn add @react-native-firebase/phone-number-verification
```

> **Android only** - This module is only available on Android. The Firebase Phone Number Verification SDK does not support iOS or web platforms. Calling any method on a non-Android platform will throw an error.

# Platform support and New Architecture

|                      |                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| **Platforms**        | **Android only**                                                                               |
| **New Architecture** | **Required** from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement). |

# What does it do

Firebase Phone Number Verification provides carrier-level phone number verification on Android devices without requiring SMS codes. It verifies the user's phone number directly through the device's SIM card and carrier network, providing a seamless and secure verification experience.

To learn more, visit the [Firebase Phone Number Verification documentation](https://firebase.google.com/docs/phone-number-verification).

Key capabilities:

- **Carrier-level verification**: Verifies phone numbers directly with the mobile carrier, without SMS.
- **Support detection**: Check whether the device and carrier support phone number verification before attempting it. This does not require user consent.
- **Verified phone number**: Retrieve the device's verified phone number as a JWT token containing the phone number, timestamps, nonce, and claims. This will present a consent dialog to the user.
- **Digital Credential API**: Supports the Android Digital Credential API for custom verification flows.

## Region & carrier limitations

Phone Number Verification depends on carrier cooperation. Not all carriers or regions are supported. Before relying on Phone Number Verification, always call `getVerificationSupportInfo()` to check support. If a SIM slot returns `reason: 'INCAPABLE_DUE_TO_CARRIER_UNSUPPORTED'`, that carrier does not participate in Phone Number Verification and you should fall back to another verification method (e.g. Firebase Auth SMS).

Common reasons verification may be unsupported:

| Reason                                 | Meaning                                                        |
| -------------------------------------- | -------------------------------------------------------------- |
| `CAPABLE`                              | The SIM's carrier supports Phone Number Verification.          |
| `INCAPABLE_DUE_TO_CARRIER_UNSUPPORTED` | The carrier does not participate in Phone Number Verification. |
| `INCAPABLE_DUE_TO_ANDROID_VERSION`     | The device's Android version is too old.                       |
| `INCAPABLE_DUE_TO_SIM_STATE`           | No SIM inserted, or SIM is in an unusable state.               |
| `CAPABILITY_STATUS_UNSPECIFIED`        | The SDK could not determine the status.                        |

# Usage

## Check verification support

Before attempting verification, check if the device's SIM card(s) support phone number verification. This call does not require user consent and can be called freely:

```js
import { getVerificationSupportInfo } from '@react-native-firebase/phone-number-verification';

const supportInfo = await getVerificationSupportInfo();

for (const info of supportInfo) {
  console.log(`SIM slot ${info.simSlot}:`);
  console.log('  Supported:', info.isSupported);
  console.log('  Carrier ID:', info.carrierId);
  console.log('  Reason:', info.reason);
}
```

The method returns an array with one entry per SIM slot. Each entry includes:

- `isSupported` — whether Phone Number Verification is available for this SIM.
- `simSlot` — the SIM slot index (0-based).
- `carrierId` — the carrier identifier string.
- `reason` — a `VerificationSupportStatus` string explaining why the SIM is or isn't supported.

### Query a specific SIM slot

On dual-SIM devices, you can query a specific SIM slot by passing the slot index:

```js
import { getVerificationSupportInfo } from '@react-native-firebase/phone-number-verification';

const supportInfo = await getVerificationSupportInfo(0); // SIM slot 0
```

### Fallback when unsupported

If Phone Number Verification is not supported, fall back to an alternative verification method:

```js
import { Platform } from 'react-native';
import {
  getVerificationSupportInfo,
  getVerifiedPhoneNumber,
} from '@react-native-firebase/phone-number-verification';

async function verifyPhoneNumber() {
  if (Platform.OS !== 'android') {
    // Use SMS-based verification on non-Android platforms
    return verifySms();
  }

  const supportInfo = await getVerificationSupportInfo();
  const supported = supportInfo.some(info => info.isSupported);

  if (supported) {
    try {
      return await getVerifiedPhoneNumber();
    } catch (error) {
      // Fall back to SMS on failure
      return verifySms();
    }
  }

  // Carrier or device doesn't support Phone Number Verification
  return verifySms();
}
```

## Verify a phone number

To initiate the full verification flow, call `getVerifiedPhoneNumber()`. This will present a consent dialog to the user asking permission to share their phone number. Your app should prepare the user for this consent screen before calling the method — for example, by explaining why their phone number is needed.

For guidance on handling user consent, see the [Firebase Phone Number Verification getting started guide](https://firebase.google.com/docs/phone-number-verification/android/get-started).

```js
import { getVerifiedPhoneNumber } from '@react-native-firebase/phone-number-verification';

try {
  const result = await getVerifiedPhoneNumber();
  console.log('Phone number:', result.phoneNumber);
  console.log('Token:', result.token);
  console.log('Expires at:', new Date(result.expirationTimestamp * 1000));
  console.log('Issued at:', new Date(result.issuedAtTimestamp * 1000));
  console.log('Nonce:', result.nonce);
  console.log('Claims:', result.claims);
} catch (error) {
  console.error('Verification failed:', error.code, error.message);
}
```

The returned result includes:

- `phoneNumber` — the verified phone number in E.164 format.
- `token` — the raw JWT token string for server-side validation.
- `expirationTimestamp` — token expiration as Unix epoch seconds.
- `issuedAtTimestamp` — token issued-at time as Unix epoch seconds.
- `nonce` — the nonce from the JWT payload, or `null`.
- `claims` — all JWT claims as a key-value map, or `null`.

## Custom verification with Digital Credentials

For advanced use cases, you can use the Digital Credential API flow:

```js
import {
  getDigitalCredentialPayload,
  exchangeCredentialResponseForPhoneNumber,
} from '@react-native-firebase/phone-number-verification';

// Step 1: Get the credential payload
const payload = await getDigitalCredentialPayload('your-unique-nonce');

// Step 2: Use the payload with Android Credential Manager
// ... (pass payload to CredentialManager API)

// Step 3: Exchange the response for a verified phone number
const result = await exchangeCredentialResponseForPhoneNumber(credentialResponse);
console.log('Phone number:', result.phoneNumber);
console.log('Expires at:', new Date(result.expirationTimestamp * 1000));
```

## Error handling

All methods reject with structured error codes from the Firebase Phone Number Verification SDK. The `error.code` property contains one of these values:

| Error Code                                | Meaning                                                         |
| ----------------------------------------- | --------------------------------------------------------------- |
| `pnv/carrier-not-supported`               | The SIM's carrier does not support Phone Number Verification.   |
| `pnv/invalid-digital-credential-response` | The Digital Credential API response was invalid.                |
| `pnv/integrity-check-failed`              | Device integrity check failed.                                  |
| `pnv/preflight-check-failed`              | Server-side preflight check failed.                             |
| `pnv/unsupported-operation`               | The API call is not supported with the given parameters.        |
| `pnv/credential-manager-error`            | Android Credential Manager failed unexpectedly.                 |
| `pnv/invalid-test-number-id`              | Test number IDs are empty, expired, or duplicated.              |
| `pnv/test-session-already-enabled`        | `enableTestSession` was called more than once.                  |
| `pnv/activity-context-required`           | An Activity context is required (app may be in the background). |

```ts
import {
  getVerifiedPhoneNumber,
  PnvErrorCode,
  type PnvError,
} from '@react-native-firebase/phone-number-verification';

try {
  const result = await getVerifiedPhoneNumber();
} catch (error) {
  const pnvError = error as PnvError;

  switch (pnvError.code) {
    case PnvErrorCode.CARRIER_NOT_SUPPORTED:
      // Fall back to SMS verification
      break;
    case PnvErrorCode.ACTIVITY_CONTEXT_REQUIRED:
      // Retry when app is in foreground
      break;
    default:
      console.error('Phone Number Verification error:', pnvError.code, pnvError.message);
  }
}
```

## Testing

To test without a real SIM card and carrier, use Firebase's test mode. This requires setup in the Firebase Console:

1. **Generate a test token**: In the Firebase Console, navigate to Phone Number Verification and generate a test token. Test tokens have a 7-day Time-To-Live.
2. **Identity and Access Management permissions**: Ensure the service account has the required `firebasepnv.testSessions.create` permission.
3. **Google system services beta**: On the test device, enroll the Google system services app into the beta channel via Google Play.
4. **Call `enableTestSession` once**: Pass the token before any verification calls. This must be called only once per app instance — calling it again will reject with `pnv/test-session-already-enabled`.

```js
import {
  enableTestSession,
  getVerifiedPhoneNumber,
} from '@react-native-firebase/phone-number-verification';

// Call once at app startup for testing
await enableTestSession('your-test-token-from-firebase-console');

// Now verification calls return test data
// Phone numbers in test mode follow the format: valid country code + all zeros
const result = await getVerifiedPhoneNumber();
console.log('Test phone number:', result.phoneNumber);
```

## Platform handling

This module is Android-only. On non-Android platforms, all methods throw an error with the message "Firebase Phone Number Verification is only supported on Android." You can guard against this using `Platform.OS`:

```js
import { Platform } from 'react-native';
import { getVerificationSupportInfo } from '@react-native-firebase/phone-number-verification';

if (Platform.OS === 'android') {
  const supportInfo = await getVerificationSupportInfo();
  // ...
}
```
```

### v6.0.0 Release

Source: https://rnfirebase.io/releases/v6.0.0

```mdx

> Please tag any GitHub issues regarding v6.0.0 with `[v6]` in the title.

This version is effectively a re-write with the goal of splitting every module into it's own package (simplifies maintenance
for contributors and also installation for users) and additionally brings each Firebase module up to ~95+% testing coverage and 100%
Firebase API Coverage.

Many of the manual native installation steps for Android & iOS have been removed and internally automated
with most modules now just 'install and go'.

The new modules:

| Name                                  |                                                    Downloads                                                     |                                                             Coverage                                                              |
| ------------------------------------- | :--------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------: |
| [Analytics](/analytics)               |    ![badge](https://img.shields.io/npm/dm/@react-native-firebase/analytics.svg?style=for-the-badge&logo=npm)     |        [![badge](https://api.rnfirebase.io/coverage/analytics/badge)](https://api.rnfirebase.io/coverage/analytics/detail)        |
| [App](/app)                           |       ![badge](https://img.shields.io/npm/dm/@react-native-firebase/app.svg?style=for-the-badge&logo=npm)        |              [![badge](https://api.rnfirebase.io/coverage/app/badge)](https://api.rnfirebase.io/coverage/app/detail)              |
| [Cloud Functions](/functions)         |    ![badge](https://img.shields.io/npm/dm/@react-native-firebase/functions.svg?style=for-the-badge&logo=npm)     |        [![badge](https://api.rnfirebase.io/coverage/functions/badge)](https://api.rnfirebase.io/coverage/functions/detail)        |
| [Cloud Firestore](/firestore)         |    ![badge](https://img.shields.io/npm/dm/@react-native-firebase/firestore.svg?style=for-the-badge&logo=npm)     |        [![badge](https://api.rnfirebase.io/coverage/firestore/badge)](https://api.rnfirebase.io/coverage/firestore/detail)        |
| [Cloud Storage](/storage)             |     ![badge](https://img.shields.io/npm/dm/@react-native-firebase/storage.svg?style=for-the-badge&logo=npm)      |          [![badge](https://api.rnfirebase.io/coverage/storage/badge)](https://api.rnfirebase.io/coverage/storage/detail)          |
| [Cloud Messaging](/messaging)         |    ![badge](https://img.shields.io/npm/dm/@react-native-firebase/messaging.svg?style=for-the-badge&logo=npm)     |        [![badge](https://api.rnfirebase.io/coverage/messaging/badge)](https://api.rnfirebase.io/coverage/messaging/detail)        |
| [Crashlytics](/crashlytics)           |   ![badge](https://img.shields.io/npm/dm/@react-native-firebase/crashlytics.svg?style=for-the-badge&logo=npm)    |      [![badge](https://api.rnfirebase.io/coverage/crashlytics/badge)](https://api.rnfirebase.io/coverage/crashlytics/detail)      |
| Dynamic Links                         |  ![badge](https://img.shields.io/npm/dm/@react-native-firebase/dynamic-links.svg?style=for-the-badge&logo=npm)   |    [![badge](https://api.rnfirebase.io/coverage/dynamic-links/badge)](https://api.rnfirebase.io/coverage/dynamic-links/detail)    |
| [In-app Messaging](/in-app-messaging) | ![badge](https://img.shields.io/npm/dm/@react-native-firebase/in-app-messaging.svg?style=for-the-badge&logo=npm) | [![badge](https://api.rnfirebase.io/coverage/in-app-messaging/badge)](https://api.rnfirebase.io/coverage/in-app-messaging/detail) |
| Instance ID                           |       ![badge](https://img.shields.io/npm/dm/@react-native-firebase/iid.svg?style=for-the-badge&logo=npm)        |              [![badge](https://api.rnfirebase.io/coverage/iid/badge)](https://api.rnfirebase.io/coverage/iid/detail)              |
| [ML](/ml)                             |        ![badge](https://img.shields.io/npm/dm/@react-native-firebase/ml.svg?style=for-the-badge&logo=npm)        |               [![badge](https://api.rnfirebase.io/coverage/ml/badge)](https://api.rnfirebase.io/coverage/ml/detail)               |
| [Performance Monitoring](/perf)       |       ![badge](https://img.shields.io/npm/dm/@react-native-firebase/perf.svg?style=for-the-badge&logo=npm)       |             [![badge](https://api.rnfirebase.io/coverage/perf/badge)](https://api.rnfirebase.io/coverage/perf/detail)             |
| [Realtime Database](/database)        |     ![badge](https://img.shields.io/npm/dm/@react-native-firebase/database.svg?style=for-the-badge&logo=npm)     |         [![badge](https://api.rnfirebase.io/coverage/database/badge)](https://api.rnfirebase.io/coverage/database/detail)         |
| [Remote Config](/remote-config)       |  ![badge](https://img.shields.io/npm/dm/@react-native-firebase/remote-config.svg?style=for-the-badge&logo=npm)   |    [![badge](https://api.rnfirebase.io/coverage/remote-config/badge)](https://api.rnfirebase.io/coverage/remote-config/detail)    |

---

The following modules are currently **migration only** for now (migrated from v5 to v6 with minimal changes), what this means:

- only some new work was done on them (e.g. migrating to v6 internals)
- only some new tests added for them (but all existing tests pass)
- flow types missing (but have TS types)

### Where is the Notifications library?

Please see [this issue](https://github.com/invertase/react-native-firebase/issues/2566) for the latest on notifications.

## Changelog

### General Library Changes

- [INTERNAL] Improved error codes & handling for all Firebase services;
  - Standardized native error to JS conversion
  - [DEVEX] Native promise rejection errors now contain additional properties to aid debugging
  - All React Native Firebase native methods should now always return an Error to JS - even if the Error occurred due to native code.
- [BUGFIX] All native events are now queued natively until a JS listener is registered. This fixes several race conditions for events like `onMessage`, `onNotification`, `onLink` etc where the event would trigger before JS was ready.
- [NEW][🔥] In an effort to further reduce manual native code changes when integrating and configuring React Native Firebase; we have added support for configuring various Firebase services & features via a `firebase.json` file in your project root.
- [NEW][ios] CocoaPods static framework support for all modules (you can use `use_frameworks!` without issues relating to this lib)
  - **Note**: Currently this has been disabled as `use_frameworks!` support in React Native was broken again in RN60. We'll re-enable in a future release for RN61.

---

### App (app)

- [NEW] Added `appConfig` & method support for `setAutomaticDataCollectionEnabled` & `automaticResourceManagement`
- [NEW] Added app `options` support for `gaTrackingId`
- [NEW] The `[DEFAULT]` Firebase app can now be safely initialized in JS, however this has some caveats;
  - Firebase services such as Performance Monitoring & Remote Config require the default app to be initialized through the plist/JSON file.
- [BREAKING] Waiting for apps to initialize via `.onReady()` has been removed. `initializeApp()` now returns a promise to the same effect
- [BREAKING] Trying to initialize the `[DEFAULT]` Firebase app in JS when it was already initialized natively will now throw an error (formerly warned)

---

### AdMob

AdMob has undergone a full rewrite to keep up-to-date with the latest changes and APIs. The JavaScript API interface has been modified from v5 to provider a simpler, cleaner way to manage ads.

- [NEW] A new `AdsConsent` helper has been added to handle user ads consent, required under GDPR regulations. See the documentation for more information.
- [NEW] Global settings can be applied to AdMob via `setRequestConfiguration`.
  - `maxAdContentRating`, `tagForChildDirectedTreatment` & `tagForUnderAgeOfConsent` are now set a global configuration settings.
- [NEW] `RewardedAd` interface used the new Google Mobile Ads SDK beta API. Rewarded ads can now be controlled from the user dashboard, supporting both video and interactive ads.
- [NEW] Added support for requesting only non-personalized ads via the `requestNonPersonalizedAdsOnly` request options.
- [NEW] Added support for custom network extras on ad requests via `networkExtras`.
  - The user reward is now pre-fetched when the ad is loaded.
- [BREAKING] The API interface for interacting with AdMob has undergone a full re-write.
- [BUGFIX] Ads can now work during React Native debugging.

---

### App Invites (invites)

- [BREAKING] this module has been deprecated by Firebase and now been removed, you should migrate to Dynamic Links.

---

### Analytics (analytics)

- [NEW] Added support for `resetAnalyticsData()`
- [NEW] Added event specific methods for many built-in analytics events, e.g. `logLevelStart`, `logSearch`, `logSignUp` and many more, see the module reference documentation for the full list of methods added.
- [INTERNAL] `setUserProperties` now iterates properties natively (formerly 1 native call per property)
- [BREAKING] all analytics methods now return a Promise, rather than formerly being 'fire and forget'

---

### Crashlytics (crashlytics)

> **Blog post announcement**: [Firebase Crashlytics for React Native](https://invertase.io/blog/react-native-firebase-crashlytics-configuration)

- [NEW] JavaScript stack traces now automatically captured and parsed
  ![JavaScript stack trace preview](https://pbs.twimg.com/media/D07RPDMW0AA7TTv.jpg:large)
- [NEW] Optionally enable automatic reporting of JavaScript unhandled Promise rejections
- [NEW] Added support for `setUserName(userName: string)`
- [NEW] Added support for `setUserEmail(userEmail: string)`
- [NEW] Added support for `isCrashlyticsCollectionEnabled: boolean`
- [NEW][android] Added support for [Crashlytics NDK](https://docs.fabric.io/android/crashlytics/ndk.html#using-gradle) reporting. This allows Crashlytics to capture Yoga related crashes generated from React Native.
- [NEW][🔥] Added `firebase.json` support for `crashlytics_ndk_enabled`, this toggles NDK support as mentioned above, defaults to `true`
- [NEW][🔥] Added `firebase.json` support for `crashlytics_debug_enabled`, this toggles Crashlytics native debug logging, defaults to `false`
- [NEW][🔥] Added `firebase.json` support for `crashlytics_auto_collection_enabled`, this toggles Crashlytics error reporting, this is useful for user opt-in first flows, e.g. set to `false` and when your user agrees to opt-in then call `setCrashlyticsCollectionEnabled(true)` in your app, defaults to `true`
- [BUGFIX][android] `crash()` now correctly crashes without being caught by a React Native red box
- [BREAKING] `setBoolValue`, `setFloatValue`, `setIntValue` & `setStringValue` have been removed and replaced with two new methods (the Crashlytics SDK converted all these into strings internally anyway):
  - `setAttribute(key: string, value: string): Promise<null>` - set a singular key value to show alongside any subsequent crash reports
  - `setAttributes(values: { [key: string]: string }): Promise<null>` - set multiple key values to show alongside any subsequent crash reports
- [BREAKING] all methods except `crash`, `log` & `recordError` now return a `Promise` that resolves when complete
- [BREAKING] `recordError(code: number, message: string)`'s function signature changed to `recordError(error: Error)` - now accepts a JS Error class instance
- [BREAKING] `setUserIdentifier()` has been renamed to `setUserId()` to match analytics implementation
- [BREAKING] `enableCrashlyticsCollection()`'s function signature changed to `setCrashlyticsCollectionEnabled(enabled: boolean)`
  - This can be used in all scenarios (formerly only able to use this when automatic initialization of Crashlytics was disabled)
  - Changes do not take effect until the next app startup
  - This persists between app restarts and only needs to be called once, can be used in conjunction with `isCrashlyticsCollectionEnabled` to reduce bridge startup traffic - though calling multiple times is still allowed

---

### Cloud Firestore (firestore)

Cloud Firestore has undergone a complete overhaul of both JavaScript & native code, including a re-write of bridge serialization, support for new features & heavy test coverage.

- [NEW] Added support for collection group queries (`firestore().collectionGroup()`).
- [NEW] Added support for `isEqual()` across most classes.
- [NEW] Added support for `SetOptions.mergeFields` (`DocumentReference.set()` / `Transaction.set()`).
- [NEW] Added support for handling snapshot metadata via the `includeMetadataChanges` flag which can be passed to `CollectionReference.onSnapshot()` and `QuerySnapshot.docChanges()` to return additional results from query snapshot listeners.
- [NEW] Cache size can now be set to unlimited using the `CACHE_SIZE_UNLIMITED` static when passed to `firestore().settings()` (also added in v5.4).
- [BUGFIX] Remove Metro circular reference warnings.
- [BUGFIX] `DocumentReference` and `CollectionReference` snapshot observers now correctly handle the same arguments as the Web SDK.
- [BUGFIX] Validate transaction gets must also have a write command (matches Web SDK).
- [BUGFIX] Setting a negative infinity value (`-Infinity`) now correctly works as expected.
- [BUGFIX] `QuerySnapshot.forEach()` can now correctly takes an optional context argument.
- [BUGFIX] Snapshot metadata now correctly returns a `SnapshotMetadata` class (as per Web SDK).
- [BUGFIX] `CollectionReference` now correctly extends a `Query` class. In v5 it is possible to chain calls from `Query` → `CollectionReference` which isn't possible on the Web SDK.
- [BUGFIX] `onSnapshot()` calls now take the correct arguments, allowing for `SnapshotListenOptions`, inline function callbacks or an object containing next/error callbacks (as per the Web SDK).
- [BUGFIX] Setting a `Date` on Firestore was setting an incorrect value. Date objects are now converted to a `Timestamp` as per the Web SDK.
- [BUGFIX] Cursor queries in v5 (`startAt`, `startAfter`, `endAt`, `endBefore`) were incorrectly handling a `DocumentSnapshot` argument. It is now possible to perform a cursor query directly on a snapshot, or on snapshot fields, as per the Web SDK, for example ending at a specific snapshot with no order.
- [BREAKING] Blob can no longer be constructed manually, as per the Web SDK.
- [BREAKING] The v6 release includes **a lot** of additional JavaScript validation. This is more consistent with the Web SDK and helps catch native errors/crashes which may occur due to false-positive data being sent over the bridge.
  - Specifically, the `Query` class has undergone a rewrite, and includes a lot of additional checks which are not present in v5. Please check your Firestore queries once upgraded.
- [BREAKING] Removed the `Query.where` single equals operator (`=`) as per the Web SDK. Use `==` instead.
- [BREAKING] previously deprecated `setTimestampsInSnapshotsEnabled` on settings has now been removed.
- [PERFORMANCE][🔥] [ANDROID] Data serialization logic is now correctly performed off the main UI thread. This will help increase performance and reduce activity on the UI thread when sending large volumes of data to Firestore and back to the device.
- [PERFORMANCE][🔥] The data serialization logic has undergone a large rewrite for performance.
  - JavaScript data being sent over the native bridge has to be converted to it's native counterpart, and visa versa. When dealing with a large number of documents and/or large amounts document data, this process can be both time consuming and resource intensive. The rewrite keeps data being sent over the bridge at a minimum; mapping data types to smaller serialization format that can be parsed by JS and Native code.
  - Sample comparisons against v5 have shown:
    - Data size sent over the bridge has been reduced by ~58%.
    - On large queries (4 documents with 1500 nested array items (containing all data types)) are over ~50% faster on v6. Smaller queries (1 document with 1500 nested array items) are over ~15% quicker.

---

### Dynamic Links (dynamicLinks)

- [BREAKING] the namespace for this module has changed, replace all usages of `firebase.links()` with `firebase.dynamicLinks()`
- [BREAKING] `onLink` & `getInitialLink` now return a `DynamicLink` object with multiple properties, formally just provided just the URL as a string
- [NEW][ios][🔥] Manually adding `AppDelegate` methods to support receiving Dynamic Link open events is no longer required, we swizzle this at runtime and automatically intercept the required events.
- [BUGFIX] Links should now always be accessible via `onLink` & `getInitialLink`
  - This fix is a 'side-effect' of the bug fix mentioned above in the `all modules` section ('`All native events are now queued natively`')
- [BREAKING] Creating a Dynamic Link builder via `new firebase.links.DynamicLink(link, domainURIPrefix)` has been deprecated, use a plain object instead as an argument for `buildLink()` & `buildShortLink()`.
- [BREAKING] Some previously allowed parameter configurations will now throw an argument error, e.g. trying to set any `DynamicLinkIOSParameters` parameter without providing an iOS bundle id will now error.
  - these configurations were incorrect to begin with but were never flagged to user code so may have gone unnoticed

---

### Functions (functions)

- [BUGFIX] Fixed an issue where `useFunctionsEmulator` does not persist natively (Firebase iOS SDK requires chaining this method before other calls and does not modify the instance, Android however persists this)

---

### In-App Messaging (inAppMessaging) - **[NEW]**

- [NEW] Added support for `firebase.inAppMessaging().isMessagesDisplaySuppressed: boolean;`
- [NEW] Added support for `firebase.inAppMessaging().setMessagesDisplaySuppressed(enabled: boolean): Promise<null>;`
- [NEW] Added support for `firebase.inAppMessaging().isAutomaticDataCollectionEnabled: boolean;`
- [NEW] Added support for `firebase.inAppMessaging().setAutomaticDataCollectionEnabled(enabled: boolean): Promise<null>;`

---

### Instance Id (iid)

- [NEW] Instance Id now supports multiple Firebase apps, e.g. `firebase.app('fooApp').iid().get()`

---

### Cloud Messaging (messaging)

- [NEW] added support for `onSendError` events, an event that indicates a message (with id) failed to send
- [NEW] added support for `onMessageSent` events, an event that indicates a message (with id) was successfully sent
- [NEW] added support for `onDeletedMessages` events, an event that indicates the FCM server deleted pending messages
  - when your app instance receives this event, it should perform a full sync with your app server if it relies on message data
- [NEW] `getToken` & `deleteToken` now optionally support `authorizedEntity` & `scope` arguments
  - `authorizedEntity` - defaults to `firebase.app().options.messagingSenderId`
  - `scope` - defaults to `FCM`
- [NEW][ios] added support for `isRegisteredForRemoteNotifications: boolean;`
- [NEW][ios] added support for `unregisterForRemoteNotifications(): Promise<void>;`
- [NEW][ios] `requestPermission` on iOS 12+ devices now uses the `UNAuthorizationOptionProvisional` option to request permission
  - this allows you to immediately start sending 'quiet' notifications to your users without their explicit permission, i.e., on a trial basis. `requestPermission` with this option will no longer show a permission request dialog to your user. [Learn More](https://developer.apple.com/documentation/usernotifications/asking-permission-to-use-notifications#Use-provisional-authorization-to-send-trial-notifications)
  - [[`WWDC 2018 Video`]](https://developer.apple.com/videos/play/wwdc2018/710/) (30:00 onwards)
- [NEW] added support for `isAutoInitEnabled: boolean;`
- [NEW] added support for `setAutoInitEnabled(enabled: boolean): Promise<void>;`
- [NEW] added support for disabling messaging auto initialization via the new `firebase.json` configuration file
  - `messaging_auto_init_enabled`: `true/false`
- [NEW][android] added support for configuring the background Headless task timeout via the new `firebase.json` configuration file
  - `messaging_android_headless_task_timeout`: `number` - milliseconds
- [NEW][android] added support for registering the background message headless task via `firebase.messaging().setBackgroundMessageHandler(handler: Function)`
- [BREAKING][android] manually registering the background message headless task handler via `AppRegistry.registerHeadlessTask` is no longer supported. Call `firebase.messaging().setBackgroundMessageHandler(handler: Function)` instead.
  - This is a preemptive change that will allow us to support background tasks for iOS in a future release (as it won't be via RN Headless Tasks as it's not supported on iOS)
- [BREAKING][android] the manually added `RNFirebaseMessagingService` service in your `AndroidManifest.xml` file is no longer required - you can safely remove it.
  - Many manual code changes that existed in v5 are now automatically handled for you in v6
- [BREAKING][ios] any the manually added `AppDelegate.m` changes for messaging on v5 are longer required - you can safely remove them (search for `RNFirebaseMessaging` in your `AppDelegate`)
  - Many manual code changes that existed in v5 are now automatically handled for you in v6
- [BREAKING] constructing a `RemoteMessage` instance via `new firebase.messaging.RemoteMessage()` is no longer supported, use `firebase.messaging().newRemoteMessage()` to retrieve a new remote message builder instance.
- [BREAKING][ios] the minimum supported iOS version is now 10
  - iOS 9 or lower only accounts for 0.% of all iPhone devices
  - to see a detailed device versions breakdown see [this link](https://david-smith.org/iosversionstats/)
  - community contributions that add iOS 9 support are welcome

---

### Performance Monitoring (perf)

The Performance Monitoring API has had a significant API change as originally highlighted would happen in the v5.x.x docs:

![image](https://user-images.githubusercontent.com/5347038/58876674-b633b780-86c6-11e9-8a74-6b6194c8ab05.png)

- [BREAKING] All `Trace` & `HttpMetric` methods (except for `start` & `stop`) are now synchronous and no longer return a Promise, extra attributes/metrics now only get sent to native when you call `stop`
- [BREAKING] `firebase.perf.Trace.incrementMetric` will now create a metric if it could not be found
- [BREAKING] `firebase.perf.Trace.getMetric` will now return 0 if a metric could not be found
- [NEW] Added support for `firebase.perf().isPerformanceCollectionEnabled: boolean`
- [NEW] Added `firebase.perf().startTrace(identifier: string): Promise<Trace>;` as a convenience method to create and immediately start a Trace

---

### Realtime Database (database)

The Realtime Database module has had a large re-write, fixing various inconsistencies against the web SDK, along with improving data serialization on the native side by moving intensive serialization work off the UI thread.

- [BREAKING][bugfix] The `Reference` class now extends a `Query` class (to match the web SDK). Currently in v5 everything is within the `Reference` class, allowing for incorrect behavior such as chaining a reference only method to a query, e.g. `ref().orderByKey().once()`. This is now not possible and will cause a standard JavaScript error.
- [BREAKING][bugfix] Internal validation for all methods has now been added. With v5 in some cases, incorrect values would be passed along to native and causing native exceptions/potential crashes.
- [BREAKING][bugfix] All query based modifiers are now validated as per the Web SDK spec. In v5 it is possible to chain queries which are not allowed together causing native errors (e.g. `.orderByKey().orderByPriority()`, `.startAt('foo', 'bar').orderByKey()` etc). Doing so in v6 will now throw an error to keep it in-line with the Web SDK.
- [BREAKING][bugfix] `Reference.push` now correctly mimics the Web SDK, returning a thenable reference.
- [NEW] `DatabaseSnapshot.forEach` now returns the current index key.
- [NEW] Many methods were missing an `onComplete` handler, which is now implemented as per the Web SDK.
- [BUGFIX] `DatabaseSnapshot.forEach` correct iterates over "array" fields in the database.

---

### Remote Config (remoteConfig)

The Remote Config API has had a significant API change as originally highlighted would happen in the v5.x.x docs:

![image](https://user-images.githubusercontent.com/5347038/58876587-7c62b100-86c6-11e9-81f9-95c26e1485a1.png)

- [BREAKING] Module namespace has been renamed to `.remoteConfig()`, replace all usages of `firebase.config` with the new name.
- [BREAKING] All Remote Config values can now be accessed synchronously in JS, see `getValue(key: string): ConfigValue` & `getAll(): ConfigValues` below
  - [BREAKING] These replace all the original async methods: `getValue`, `getValues`, `getKeysByPrefix`
- [BREAKING] `setDefaultsFromResource` now returns a Promise that resolves when completed, this will reject with code `config/resource_not_found` if the file could not be found
- [BREAKING] `setDefaultsFromResource` now expects a resource file name for Android to match iOS, formerly this required a resource id (something you would not have in RN as this was generated at build time by Android)
  - And example for both platforms can be found in the tests.
- [BREAKING] `enableDeveloperMode` has been removed, you can now use `setConfigSettings({ isDeveloperModeEnabled: boolean })` instead
- [BREAKING] `setDefaults` now returns a Promise that resolves when completed
- [NEW] Added a new `fetchAndActivate` method - this fetches the config and activates it without the need to call `activate()` separately
- [NEW] Added the following properties to `firebase.remoteConfig()`; `lastFetchTime`, `lastFetchStatus` & `isDeveloperModeEnabled`
- [NEW] Added a new `setConfigSettings` method - this allows setting `isDeveloperModeEnabled`, replaces the `enableDeveloperMode` method
  - This is a generic settings function to preemptively account for an upcoming future change to the native SDKs - more settings to be added.
- [NEW] All previous `get*` methods have been removed and replaced with 2 synchronous methods:
  - `getValue(key: string): ConfigValue` - returns a single configuration value `{ value, source }`
  - `getAll(): ConfigValues` - returns all configuration values e.g. `{ some_key: { value, source }, other_key: { value, source } }`

> **Note**: Multi-apps is not yet supported as the Firebase iOS SDK is missing support for it.

---

### Cloud Storage (storage)

- [NEW] Added support for `put` (`Blob` | `ArrayBuffer` | `Uint8Array`)
  - `contentType` mime type is automatically inferred from `Blob`
- [NEW] Added support for `putString` and all string formats (raw, `base64`, `base64url` & `data_url`)
  - `contentType` mime type is automatically inferred from `data_url` strings
- [NEW] Added support multiple buckets, e.g. `firebase.app().storage('gs://my-other-bucket')`
- [NEW] Added support `pause()`, `resume()` & `cancel()` for Upload & Download Storage tasks
- [NEW] Added an `error` property to `TaskSnapshot` for `error` state events - this is an instance of `NativeFirebaseError` (with `code` & `message`)
- [NEW] Added support for `StorageReference.list()` & `StorageReference.listAll()`.
- [BREAKING] Removed formerly deprecated `UploadTaskSnapshot.downloadUrl` property, use `StorageReference.getDownloadURL(): Promise<string>` instead
- [BREAKING] `StorageReference.downloadFile()` is now deprecated and will be removed in a later release, please rename usages of this to `writeToFile()` - renamed to match Native SDKs
- [BREAKING] `firebase.storage.Native` has moved to `firebase.utils.Native`
- [BREAKING] `firebase.utils.Native` is now deprecated and will be removed in a later release, please rename usages of this to `firebase.utils.FilePath`
- [BREAKING] `firebase.utils.Native.*` some properties have been renamed and deprecated and will be removed in a later release, follow the in-app console warnings on how to migrate
- [BUGFIX][android] Update/set metadata now correctly supports removing metadata values by passing a null property value in `customMetadata`
- [BUGFIX][android] `contentType` mime type is now correctly determined in all scenarios, there was an edge case where it would just use the default value
- [INTERNAL][android] `downloadFile` no longer uses a `StreamDownloadTask`, replaced with the newer `FileDownloadTask`

---

### ML (Machine Learning)

> This is a new module in React Native Firebase.

- [NEW] Implemented support for language identification APIs
  - Single Languages: `identifyLanguage()`.
  - Multiple Languages: `identifyPossibleLanguages()`
- [NEW] Implemented support for [Text Recognition](https://firebase.google.com/docs/ml/recognize-text) Vision APIs;
- [NEW] Implemented support for [Document Text Recognition](https://firebase.google.com/docs/ml/recognize-text) Vision APIs;
- [NEW] Implemented support for [Image Labeling](https://firebase.google.com/docs/ml/label-images) Vision APIs;
- [NEW] Implemented support for [Landmark Recognition](https://firebase.google.com/docs/ml/recognize-landmarks) Vision APIs;

---

### Utils

- [NEW] Added support via `isRunningInTestLab` for checking if an Android application is running inside a Firebase Test Lab environment
- [NEW] Added a new `FilePath` utility that provides common file paths on the device, see `firebase.utils.FilePath` docs for more info, this is the replacement API for `firebase.storage.Native`
```

### v6.0.1 Release

Source: https://rnfirebase.io/releases/v6.0.1

```mdx

> Please tag any GitHub issues regarding v6 with `[v6]` in the title.

🐞 This is a bug fix release. 🐞

- Fix [#2635](https://github.com/invertase/react-native-firebase/issues/2635); Android build error; Cannot get property 'parentFile' on null object when trying to detect a `firebase.json` file
- Fix [#2648](https://github.com/invertase/react-native-firebase/issues/2648); TypeScript; Auto complete not working with VSCode

## Authentication

- Fix [#2639](https://github.com/invertase/react-native-firebase/issues/2639); Android; Casting error from JS to native when calling `auth().verifyPhoneNumber(phoneNumber)`
- Fix [#2693](https://github.com/invertase/react-native-firebase/issues/2693); iOS; Can't use `verifyPhoneNumber`

## Crashlytics

- Fix [#2307](https://github.com/invertase/react-native-firebase/issues/2307); Android,iOS; `crashlytics().setUserId` crashes when `auto_collection` disabled

## Firestore

- Fix [#2654](https://github.com/invertase/react-native-firebase/issues/2654); Firestore settings not applied
- Fix [#2532](https://github.com/invertase/react-native-firebase/issues/2532); `FieldPath` doesn't deep merge
- Fix [#2581](https://github.com/invertase/react-native-firebase/issues/2581); Index Creation Error Message Surfacing
- Fix [#2691](https://github.com/invertase/react-native-firebase/issues/2691); `FirestoreQuery`/`FirestoreQueryModifiers` incorrectly mutating previous query instances when chaining
- Fix [#2681](https://github.com/invertase/react-native-firebase/issues/2681); NPE in exception handling

## Realtime Database

- Fix `DatabaseQuery`/`DatabaseQueryModifiers` incorrectly mutating previous query instances when chaining

## Vision

- Fix [#2666](https://github.com/invertase/react-native-firebase/issues/2666); `textRecognizerProcessImage()` errors with `vision/file-not-found` when using `file://` paths
```

### v6.0.2 Release

Source: https://rnfirebase.io/releases/v6.0.2

```mdx

> Please tag any GitHub issues regarding v6 with `[v6]` in the title.

🐞 This is a bug fix release. 🐞

- [iOS] Fixed an issue where native event listeners were not cleared after using React Native reload

## Analytics

- [TS] Fix several incorrect type definitions

## Authentication

- Fix [#2639](https://github.com/invertase/react-native-firebase/issues/2639); Create a stack trace for usage with `NativeFirebaseError`
- Fix [#2713](https://github.com/invertase/react-native-firebase/issues/2713); iOS auth event subscriptions not correctly removing on reload

## Firestore

- Fix [#2719](https://github.com/invertase/react-native-firebase/issues/2719); Only apply `id` cursor when no `order` query modifiers

## ML Vision

- Fix [#2744](https://github.com/invertase/react-native-firebase/issues/2744); improve `nil` checks on creating dictionary instances
```

### v6.0.3 Release

Source: https://rnfirebase.io/releases/v6.0.3

```mdx

> Please tag any GitHub issues regarding v6 with `[v6]` in the title.

🐞 This is a bug fix release. 🐞

## Database

- [iOS] Fix a crash in development when reloading React Native; [#2770](https://github.com/invertase/react-native-firebase/pull/2770),[#2772](https://github.com/invertase/react-native-firebase/pull/2772).

## Firestore

- [TS] Fix incorrect `QuerySnapshot` `forEach` types

## Storage

- [iOS] Fixed an issue where uploading files from `Photos` would fail to locate the asset (`ph://` files)
```

### v6.1.0 Release

Source: https://rnfirebase.io/releases/v6.1.0

```mdx

> Please tag any GitHub issues regarding v6 with `[v6]` in the title.

🐞 This is a bug fix and feature release. 🐞

## Features

### SDK & Dependencies Updates

- Update Firebase Android `BOM` SDK version to v28.0.3 ([#2868](https://github.com/invertase/react-native-firebase/issues/2868)) ([42e034c](https://github.com/invertase/react-native-firebase/commit/42e034c4807da54441d2baeab9f57bbf1a137a4a))
- Update Firebase iOS SDK versions to v6.13.0 ([547d0a2](https://github.com/invertase/react-native-firebase/commit/547d0a2d74a68808b29063f9b3aa3e1ac38551fc))
- Update new project template to React Native 0.61.5, from 0.60
  - ([3e90981](https://github.com/invertase/react-native-firebase/commit/3e909813fb1b14a3baeb3468cb5e78ea86503f60))
  - ([#2821](https://github.com/invertase/react-native-firebase/issues/2821)) ([fb4941b](https://github.com/invertase/react-native-firebase/commit/fb4941b6e5dc6b3101eeaa2c1c429300a3e05da7))

### Firestore

- Add support for `array-contains`, `array-contains-any` & `in` queries ([#2868](https://github.com/invertase/react-native-firebase/issues/2868)) ([42e034c](https://github.com/invertase/react-native-firebase/commit/42e034c4807da54441d2baeab9f57bbf1a137a4a))
  - [Learn more about these new querying features here.](https://firebase.googleblog.com/2019/11/cloud-firestore-now-supports-in-queries.html)

### Remote Config

- Add support for the `minimumFetchInterval` config setting ([#2789](https://github.com/invertase/react-native-firebase/issues/2789)) ([57965e7](https://github.com/invertase/react-native-firebase/commit/57965e73a7e1089335c5446fb91cd44c1b19725d)), closes [/github.com/firebase/firebase-ios-sdk/blob/main/FirebaseRemoteConfig/Sources/Public/FirebaseRemoteConfig/FIRRemoteConfig.h#L148-L149](https://github.com/firebase/firebase-ios-sdk/blob/main/FirebaseRemoteConfig/Sources/Public/FirebaseRemoteConfig/FIRRemoteConfig.h#L148-L149)

## Bug Fixes

- **`admob`:** add null checks for `getCurrentActivity()` usages ([#2913](https://github.com/invertase/react-native-firebase/issues/2913)) ([1fb296d](https://github.com/invertase/react-native-firebase/commit/1fb296dc3bc2ffcf2db1d09f5f17b0209ff8276a))
- **`admob,iOS`:** use `AdMob` vs `Admob` for Pod name ([#2922](https://github.com/invertase/react-native-firebase/issues/2922)) ([88a0167](https://github.com/invertase/react-native-firebase/commit/88a01672a8e443e87c7e1513cdb0d0594dd47ed9))
- **`analytics`:** TypeScript `logEvent` parameters argument should be optional ([#2822](https://github.com/invertase/react-native-firebase/issues/2822)) ([3b8757c](https://github.com/invertase/react-native-firebase/commit/3b8757c0d4f6787c2e5f1ca2c04e73e809d3deae))
- **`analytics`:** use correct `add_to_cart` event name ([#2882](https://github.com/invertase/react-native-firebase/issues/2882)) ([2369c62](https://github.com/invertase/react-native-firebase/commit/2369c629fc21705f32f2a4b6487260e3ab05569e))
- **`auth`:** collection was mutated while being enumerated. ([#2900](https://github.com/invertase/react-native-firebase/issues/2900)) ([5471187](https://github.com/invertase/react-native-firebase/commit/5471187b30527cd1157bde209886664e52413a7c))
- **`auth`:** don't mutate modifiers ordering when building query key (fixes [#2833](https://github.com/invertase/react-native-firebase/issues/2833)) ([9df493e](https://github.com/invertase/react-native-firebase/commit/9df493e837b6a709b8f61027690219738ffa830a))
- **`auth`:** fix exception in `PhoneAuthListener` ([#2828](https://github.com/invertase/react-native-firebase/issues/2828)) ([0843cbd](https://github.com/invertase/react-native-firebase/commit/0843cbdf3a4548c78a93bed115a1b3b0666436d1)), closes [#2639](https://github.com/invertase/react-native-firebase/issues/2639)
- **`auth`:** trigger initial listener asynchronously ([#2897](https://github.com/invertase/react-native-firebase/issues/2897)) ([227ab63](https://github.com/invertase/react-native-firebase/commit/227ab631a6163a950af675da690b1467f7616d6c))
- **`crashlytics`:** `setCrashlyticsCollectionEnabled` return promise ([#2792](https://github.com/invertase/react-native-firebase/issues/2792)) ([4c19b94](https://github.com/invertase/react-native-firebase/commit/4c19b9439ddf6ecf57e59f7e2d8b64954678d8e5))
- **`database,android`:** fix issue where transaction signal state error not caught ([d7252a2](https://github.com/invertase/react-native-firebase/commit/d7252a2d4e1987114ab1a8e5c04f0088a86d2b5b))
- **`database,iOS`:** return null snapshot key if does not exist (fixes [#2813](https://github.com/invertase/react-native-firebase/issues/2813)) ([bbf3df9](https://github.com/invertase/react-native-firebase/commit/bbf3df98ab88559de1392cba7163666a31e98ee3))
- **`firestore`:** correctly apply internal `__name__` query modifier ([#2866](https://github.com/invertase/react-native-firebase/issues/2866)) ([a5da010](https://github.com/invertase/react-native-firebase/commit/a5da0107ff570dc6327bb3ae5d7fff4143183ac9)), closes [#2854](https://github.com/invertase/react-native-firebase/issues/2854)
- **`firestore,iOS`:** settings incorrectly set multiple times ([#2869](https://github.com/invertase/react-native-firebase/issues/2869)) ([ed858c9](https://github.com/invertase/react-native-firebase/commit/ed858c96eee0bcfa796faf3f151116c35a4328c0))
- **`messaging`:** `onTokenRefresh(event => event.token)` fixes [#2889](https://github.com/invertase/react-native-firebase/issues/2889) ([1940d6c](https://github.com/invertase/react-native-firebase/commit/1940d6c8fbab64ccf739186cea9633a605237942))
- **`messaging`:** typo in `isRegisteredForRemoteNotifications` ([#2645](https://github.com/invertase/react-native-firebase/issues/2645)) ([f0e614f](https://github.com/invertase/react-native-firebase/commit/f0e614f48567645e89e837ee56d3f3d251473b09)), closes [/github.com/invertase/react-native-firebase/blob/main/packages/messaging/ios/RNFBMessaging/RNFBMessagingModule.m#L58](https://github.com/invertase/react-native-firebase/blob/ae03f3f0be636fcd949965ee720a691f8582ef82/packages/messaging/ios/RNFBMessaging/RNFBMessagingModule.m#L58)
- **`messaging,iOS`:** `hasPermission` checks `authorizationStatus` ([#2908](https://github.com/invertase/react-native-firebase/issues/2908)) ([7cab58d](https://github.com/invertase/react-native-firebase/commit/7cab58d87fcba592c697a3441bd77033eb09ab3c))
- **`messaging,iOS`:** wait for remote notification registration status ([8c339d1](https://github.com/invertase/react-native-firebase/commit/8c339d10e288ef60e83e38bc4a245c5a251c83ff)), closes [#2657](https://github.com/invertase/react-native-firebase/issues/2657)
- **`storage`:** fix video asset resources on iOS13 ([#2750](https://github.com/invertase/react-native-firebase/issues/2750)) ([fded286](https://github.com/invertase/react-native-firebase/commit/fded28621fb5c73c3daba009cc4f2ef6fde21745))
- **`storage,iOS`:** handle null Storage metadata values ([#2875](https://github.com/invertase/react-native-firebase/issues/2875)) ([26f752a](https://github.com/invertase/react-native-firebase/commit/26f752a1172a36e7c5ea837c1792610fd37adbb4))
- **`storage,iOS`:** handle null Storage metadata values ([#2881](https://github.com/invertase/react-native-firebase/issues/2881)) ([eeb90c0](https://github.com/invertase/react-native-firebase/commit/eeb90c0a376e88f4ceb20a1dc5fd3bb4ce558a61))
- **`storage,iOS`:** use long value for `maxResults` list option (fixes [#2804](https://github.com/invertase/react-native-firebase/issues/2804)) ([9488103](https://github.com/invertase/react-native-firebase/commit/94881037e0d304e3a585088be1dcae42be8794a8))
- **`storage,js`:** validate that list `maxResults` is an integer value ([2fc9e9d](https://github.com/invertase/react-native-firebase/commit/2fc9e9d537e954989a50f941e2479fbbdb3874c9))
- **`template`:** fix invalid flow config file ([1def1c1](https://github.com/invertase/react-native-firebase/commit/1def1c1ce5ee320e7ff8d490e9e711281f5abdda))
- **`template`:** add `noCompress` `tflite` by default to android template (for [#2478](https://github.com/invertase/react-native-firebase/issues/2478)) ([9dd3fa6](https://github.com/invertase/react-native-firebase/commit/9dd3fa68c30b8b2f687bae4d9e81f438311ae739))
```

### v6.2.0 Release

Source: https://rnfirebase.io/releases/v6.2.0

```mdx

> Please tag any GitHub issues regarding v6 with `[v6]` in the title.

This priority release implements Apple Authentication support for Firebase Authentication (iOS only);

Recently the [App Store policy changed](https://developer.apple.com/news/?id=09122019b); apps that provide social
authentication on iOS must also provide support for Apple Authentication. All new apps submitted to the App Store must
now follow these guidelines. Existing apps and app updates have until April 2020 to follow them.

To help integrate this in your apps we've also built a new React Native library.

---

## React Native Apple Authentication

[React Native Apple Authentication](https://github.com/invertase/react-native-apple-authentication) is a React Native
library that provides access to the Apple Authentication APIs and Button components on iOS and, integrates well with
React Native Firebase.

![Apple Authentication library for React Native](https://static.invertase.io/assets/apple-auth.png)

To help you get started with integrating Apple Authentication we've included a small guide/example specific to Firebase
on the repository.

[[Guide: Usage with React Native Firebase]](https://github.com/invertase/react-native-apple-authentication/blob/main/docs/FIREBASE.md)
```

### v6.3.0 Release

Source: https://rnfirebase.io/releases/v6.3.0

```mdx

> Please tag any GitHub issues regarding v6 with `[v6]` in the title.

🐞 This is a bug fix and feature release. 🐞

## Features

- Integrated `userAccessGroup` iOS method into the `auth` module ([`#3074`](https://github.com/invertase/react-native-firebase/issues/3074)) ([`#044711c`](https://github.com/invertase/react-native-firebase/commit/044711cf7d70d65c1ecda039f047d2a6bf304770))

### SDK & Dependencies Updates

- Update iOS dependencies, `Crashlytics` to v3.14.0 & `Fabric` to v1.10.2 ([`#3012`](https://github.com/invertase/react-native-firebase/issues/3012)) ([`#3901634`](https://github.com/invertase/react-native-firebase/commit/39016346e419175119e863b2e2bff10166ddf40c))

### Firestore

- Add `QueryDocumentSnapshot` interface ([`#5de3770`](https://github.com/invertase/react-native-firebase/commit/5de37708daead91b849674b12fa5da761cbaf649))

## Bug Fixes

- **`admob`:** add missing `null` checks (([`#2912`](https://github.com/invertase/react-native-firebase/issues/2912)) ([`#b5243cf`](https://github.com/invertase/react-native-firebase/commit/b5243cf25a130d10160635c23846a20435995cad))

- **`admob`:** set correct loading variable to allow multiple AdMob instances (([`#3185`](https://github.com/invertase/react-native-firebase/issues/3185)) ([`#af768e3`](https://github.com/invertase/react-native-firebase/commit/af768e3eb57975bec8b4c0f0f50dd0f9e7418e27))

- **`analytics`:** fixed dynamic linking bug whenever analytics parameters are present (([`#3086`](https://github.com/invertase/react-native-firebase/issues/3086)) ([`#716d472`](https://github.com/invertase/react-native-firebase/commit/716d47262098c1ea3dcf56aaa8e04a4dcf0de6be))

- **`app`:** fix export to correct syntax for valid TypeScript parsing ([`#2e2b24e`](https://github.com/invertase/react-native-firebase/commit/2e2b24e51d43524c9ec5c7becd75b7dcbaca30be))

- **`app`:** remove `Object.freeze` from app to allow `redux-firestore` to work ([`#2733`](https://github.com/invertase/react-native-firebase/issues/2733)) ([`#46be1b0`](https://github.com/invertase/react-native-firebase/commit/46be1b0c996e976357f1190bede29559be94a162))

- **`auth`:** fix default parameter for sending sign in link to email ([`#239b35b`](https://github.com/invertase/react-native-firebase/commit/239b35b362289629fa4c46aa792f9b4200545d24))

- **`auth`:** `sendPasswordResetEmail` second argument is `null` by default to signal no settings ([`#3198`](https://github.com/invertase/react-native-firebase/issues/3198)) ([`#39ceba3`](https://github.com/invertase/react-native-firebase/commit/39ceba33eccf8f4dc6e0f3b4805f0034419742a9))

- **`crashlytics`:** fix issue where `fileName` variable could be undefined ([`#3079`](https://github.com/invertase/react-native-firebase/issues/3079)) ([`#1813e14`](https://github.com/invertase/react-native-firebase/commit/1813e14d581ef8f8a50606388468a98c046ac818))

- **`crashlytics`:** JavaScript source maps now available in Crashlytics ([`#3084`](https://github.com/invertase/react-native-firebase/issues/3084)) ([`#036a50e`](https://github.com/invertase/react-native-firebase/commit/036a50e7b8328ab51ee202300f91069edd2f4cf2))

- **`database`:** add key to the `Reference` type for (([`#3072`](https://github.com/invertase/react-native-firebase/issues/3072)) ([`#b8490a5`](https://github.com/invertase/react-native-firebase/commit/b8490a58a8844d88cd944e0b1d8d2fa3dfb3418f))

- **`firestore`:** offline Android lookup was crashing when looking up a document ([`#2757`](https://github.com/invertase/react-native-firebase/issues/2757)) ([`#c66bfc6`](https://github.com/invertase/react-native-firebase/commit/c66bfc61db8538cc9c1b15fa8a1c46f4cdbc580b))

- **`firestore`:** Can use `in` operator when using `where()` for numbers ([`#3004`](https://github.com/invertase/react-native-firebase/issues/3004)) ([`#13a6560`](https://github.com/invertase/react-native-firebase/commit/13a6560a403b353c46dff0a0a8c52fb64241f4f8))

- **`firestore`:** collection group queries with document snapshot as bound works ([`#3063`](https://github.com/invertase/react-native-firebase/issues/3063)) ([`#a3aaff3`](https://github.com/invertase/react-native-firebase/commit/a3aaff353f173a386af77c7302c7b23e55b28f2c))

- **`firestore`:** handle `FieldPath` as array value ([`#3178`](https://github.com/invertase/react-native-firebase/issues/3178)) ([`#2cb6d44`](https://github.com/invertase/react-native-firebase/commit/2cb6d44b77051f3831ed52b2687ce254d407904d))

- **`ios`:** handle `Info.plist` file paths with spaces ([`#ceddf99`](https://github.com/invertase/react-native-firebase/commit/ceddf996739204ce2c971eb1819bf11640b1cace))

- **`ios`:** use correct import of `RCTBridgeModule.h` ([`#7db4cd8`](https://github.com/invertase/react-native-firebase/commit/7db4cd883ab71f40fd8c9886c80d7e7489acbcc2))

- **`messaging`:** corrected TypeScript signature for `setBackgroundMessageHandler` ([`#a2879b6`](https://github.com/invertase/react-native-firebase/commit/a2879b60fda86232737a437f3c74110d4652aacd))

- **`perf`:** fixed a bug which stopped custom performance metrics from working ([`#3119`](https://github.com/invertase/react-native-firebase/issues/3119)) ([`#1e56721`](https://github.com/invertase/react-native-firebase/commit/1e567214e95b199c8d7a2ed2f804cffd83a89510))
```

### v6.4.0

Source: https://rnfirebase.io/releases/v6.4.0

```mdx

## Features

- `iOS`: added a `Podfile` option to allow using React Native Firebase packages as static frameworks ([`#3253`](https://github.com/invertase/react-native-firebase/issues/3253)) ([`#530f8bb`](https://github.com/invertase/react-native-firebase/commit/530f8bbb51f89f106854dbf1df5ec80211e2cf8b))
  - see [Allow iOS Static Frameworks](/#allow-ios-static-frameworks) to learn more. This option will default to true in the next major release.

### Messaging

- added support for `onNotificationOpenedApp` & `getInitialNotification` APIs.
  - these can be used to detect if a user opened the app via pressing a notification. ([`#d66a611`](https://github.com/invertase/react-native-firebase/commit/d66a6118f82005087f53b86571990fc071402153))
- the `RemoteMessage` event (e.g. via `onMessage`) now includes a notification payload (if present). ([`#d66a611`](https://github.com/invertase/react-native-firebase/commit/d66a6118f82005087f53b86571990fc071402153))
- `setBackgroundMessageHandler` now supported on iOS. ([`#d66a611`](https://github.com/invertase/react-native-firebase/commit/d66a6118f82005087f53b86571990fc071402153))
- the iOS permissions API has been upgraded to now support custom permissions.
  - the permission API selects sensible defaults, however; allows you to fully customize them if required.
  - provisional permissions are now disabled by default (previously, it was enabled by default for iOS 12+ devices). ([`#d66a611`](https://github.com/invertase/react-native-firebase/commit/d66a6118f82005087f53b86571990fc071402153))
- the `requestPermission` & `hasPermission` APIs now returns the current authorization status as an enum, rather than a boolean value.
  - see [`AuthorizationStatus`](https://invertase.github.io/react-native-firebase/_react-native-firebase/messaging/modular/AuthorizationStatus.html) reference documentation for available enum members

---

## Bug Fixes

- `iOS`: update `in-app-messaging` dependency to latest package name in iOS `InAppMessaging` ([`#166692d`](https://github.com/invertase/react-native-firebase/commit/166692d68ef396f3e8664edd7feab7c80038004b))

### Analytics

- logging event parameters can be objects and arrays ([`#2876`](https://github.com/invertase/react-native-firebase/issues/2876)) ([`#eebfb04`](https://github.com/invertase/react-native-firebase/commit/eebfb04a7c0a856a9d5d311ae99138df9ab90c3b))
- update `logEvent` types ([`#65ec7eb`](https://github.com/invertase/react-native-firebase/commit/65ec7eb431712f8c4d3cf96c24489e6a13ef4e13))

### Authentication

- `user.metadata.lastSignInTime` & `user.metadata.creationTime` now correctly return an ISO date string ([`#2555`](https://github.com/invertase/react-native-firebase/issues/2555)) ([`#8adef65`](https://github.com/invertase/react-native-firebase/commit/8adef653faa008e0146374f99f5ba1af902749bf))

### Dynamic Links

- switch `ShortLinkType` interface to an enum ([`#870d0a1`](https://github.com/invertase/react-native-firebase/commit/870d0a198692c65d2857765d1b216738ec74856f))
- fix links options not correctly applied in iOS ([`#54bc6f8`](https://github.com/invertase/react-native-firebase/commit/54bc6f8403b12a8cfaf0b862d13310ef28076d06))
- fix missing return statement causing bug in iOS ([`#3315`](https://github.com/invertase/react-native-firebase/issues/3315)) ([`#1661f6e`](https://github.com/invertase/react-native-firebase/commit/1661f6e084c47ed835cc4539c654286964a6d9a8))

### Firestore

- improve Firestore query validation;
- `where` field parameter cannot match `orderBy` parameter ([`#6311dc8`](https://github.com/invertase/react-native-firebase/commit/6311dc8f68e6cf0605d2f306885d2fbc0ef779d6))
- field path cannot be used with a `orderBy` parameter ([`#ea19622`](https://github.com/invertase/react-native-firebase/commit/b90a736fc8f9a1b25239bb68e5a62de711b673c7))

### Functions

- correctly throw an `UNAVAILABLE` error code on network IO errors for Android to match iOS ([`#3310`](https://github.com/invertase/react-native-firebase/issues/3310)) ([`#7351147`](https://github.com/invertase/react-native-firebase/commit/73511472bd7690158f3d9924d5f4d8c0cad69910))

### Messaging

- `onMessage` now works correctly for `data-only`, `notification-only` & `data-notification` messages. Previously it only worked for `data-only` messages
- `setBackgroundMessageHandler` now works when the app fully quit or backgrounded, for `data-only`, `notification-only` & `data-notification` messages. Previously, when the app was fully quit, events were not being handled
  - for iOS this requires `content-available` to be set
- add better handling for messages in cases where no user handler has been set
- `Android`: ensure a default notification color is always present when user does not set one
- `iOS`: calling `registerDeviceForRemoteMessages`/`registerForRemoteNotifications` was incorrectly causing permissions to be requested before explicitly requesting them via the messaging API
- `iOS`: registering the device was not being called if it was already registered internally.
  - Devices should **always** register with `registerDeviceForRemoteMessages`, as per Apple guidelines, regardless of current registration status.
  - Make sure you **always** call `registerDeviceForRemoteMessages` during your app initialization on iOS
- `iOS`: in cases where requesting an FCM with the default `scope` & `authorizedEntity`, the underlying code now uses the recommended `instanceIDWithHandler` vs `tokenWithAuthorizedEntity`.
  - This fixes an issue where FCM would throw a `"The operation couldn’t be completed"` error ([`#2657`](https://github.com/invertase/react-native-firebase/issues/2657))
- `iOS`: direct FCM connection is now fixed.
  - When the app was in the foreground, `data-only` messages were not coming through, they are now.
- `iOS`: when running debug build, the APNs token will be registered with FCM as a `"sandbox"` key type
- `iOS`: the original APNs swizzling we implemented was not functioning correctly with `application:didReceiveRemoteNotification:fetchCompletionHandler:`.
  - We added additional logic whereby this is executed in all scenarios (foreground/background/quit) and replaces a deprecated Apple API.
  - This fixes issues with `data-only` messages not being handled by the device
- `iOS`: any custom `FIRMessagingDelegate` methods you add to your `AppDelegate.m` will now also be called internally by React Native Firebase messaging.

### Storage

- fixed a bug that occurred when parsing encoded URLs ([`#2753`](https://github.com/invertase/react-native-firebase/issues/2753)) ([`#8e99b9c`](https://github.com/invertase/react-native-firebase/commit/8e99b9cb9093ba0cc3aadcb56127c8500ea8bf36))
- storage tasks now correctly have a `snapshot` property - to match the Web SDK
- `put` method now correctly returns a thenable `Task` instance instead of a promise ([`#ea19622`](https://github.com/invertase/react-native-firebase/commit/ea1962270b6c20d5b15dbaaea5c4d88a0a4ae3e2))

---

## Deprecations

### Messaging

- `registerForRemoteNotifications` has been deprecated in favor of `registerDeviceForRemoteMessages`.
  - It will be removed in a following major version. Underlying functionality has not changed - renamed to avoid confusion with messages vs notifications. ([`#d66a611`](https://github.com/invertase/react-native-firebase/commit/d66a6118f82005087f53b86571990fc071402153))
- `isRegisteredForRemoteNotifications` has been deprecated in favor of `isDeviceRegisteredForRemoteMessages`.
  - It will be removed in a following major version. Underlying functionality has not changed - renamed to avoid confusion with messages vs notifications. ([`#d66a611`](https://github.com/invertase/react-native-firebase/commit/d66a6118f82005087f53b86571990fc071402153))
- `unregisterForRemoteNotifications` has been deprecated in favor of `unregisterDeviceForRemoteMessages`.
  - It will be removed in a following major version. Underlying functionality has not changed - renamed to avoid confusion with messages vs notifications. ([`#d66a611`](https://github.com/invertase/react-native-firebase/commit/d66a6118f82005087f53b86571990fc071402153))
```

### Remote Config

Source: https://rnfirebase.io/remote-config/usage

```mdx

# Installation

This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app" module, view the
[Getting Started](/) documentation.

This module also requires that the `@react-native-firebase/analytics` module is already setup and installed. To install the "analytics" module, view it's [Getting Started](/analytics/usage) documentation.

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the remote-config module
yarn add @react-native-firebase/remote-config

# If you're developing your app using iOS, run this command
cd ios/ && pod install
```

If you're using an older version of React Native without autolinking support, or wish to integrate into an existing project,
you can follow the manual installation steps for [iOS](/remote-config/usage/installation/ios) and [Android](/remote-config/usage/installation/android).

# Platform support and New Architecture

|                      |                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| **Platforms**        | Android, iOS (native Firebase SDK)                                                             |
| **New Architecture** | **Required** from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement). |

**Platform notes:** `reset()` is **Android only** — iOS does not clear activated, fetched, or default Remote Config values. `setDefaultsFromResource` loads from native `.plist` / XML resource files.

# What does it do

Remote Config allows you to change the appearance and/or functionality of your app without requiring an app update.
Remote Config values are input into the Firebase console and accessible via a JavaScript API. This gives you full control
over when and how these Remote Config values are applied and affect your application.

<YouTube id="_CXXVFPO6f0" />

# Usage

To get started, you need to define some parameters over on the [Firebase Console](https://console.firebase.google.com/project/_/config).

![Firebase Console - Remote Config](https://images.prismic.io/invertase/87dc40bd-0da7-4d83-a87c-b12698b9818f_remote-config-console.png?auto=compress,format)

Each parameter is assigned a unique "key" and values. The values can be broken down to target specific conditions (such as Android or iOS). In the above example,
only Android devices would receive `enabled` for the `awesome_new_feature` parameter.

## Default values

Before fetching the parameters from Firebase, it is first important to set some default values. Default values
help ensure that your application code runs as expected in scenarios where the device has not yet retrieved the values.

An example of this is having no network or you have not yet fetched them within your own code.

Setting default values helps to ensure that both the local device & Firebase servers are both in sync. Call the
`setDefaults` method early on in your application:

```js
import React, { useEffect } from 'react';
import { getRemoteConfig } from '@react-native-firebase/remote-config';

function App() {
  useEffect(() => {
    const remoteConfig = getRemoteConfig();
    remoteConfig.defaultConfig = {
      awesome_new_feature: 'disabled',
    };
    console.log('Default values set.');
  }, []);
}
```

## Fetch & Activate

Before reading the values from Firebase, we first need to pull them from Firebase (fetching) & then enable them on
the device (activating). The `fetchAndActivate` API combines both tasks into a single flow:

```js
import { getRemoteConfig, fetchAndActivate } from '@react-native-firebase/remote-config';

const remoteConfig = getRemoteConfig();
remoteConfig.defaultConfig = {
  awesome_new_feature: 'disabled',
};

fetchAndActivate(remoteConfig).then(fetchedRemotely => {
  if (fetchedRemotely) {
    console.log('Configs were retrieved from the backend and activated.');
  } else {
    console.log(
      'No configs were fetched from the backend, and the local configs were already activated',
    );
  }
});
```

## Reading values

With the defaults set and the remote values fetched from Firebase, we can now use the `getValue` method to get the
value and use a number of methods to retrieve the value (same API as Firebase Remote Config web SDK)

```js
import { getRemoteConfig, getValue } from '@react-native-firebase/remote-config';

const awesomeNewFeature = getValue(getRemoteConfig(), 'awesome_new_feature');

// resolves value to string
if (awesomeNewFeature.asString() === 'enabled') {
  enableAwesomeNewFeature();
}
// resolves value to number
// if it is not a number or source is 'static', the value will be 0
if (awesomeNewFeature.asNumber() === 5) {
  enableAwesomeNewFeature();
}
// resolves value to boolean
// if value is any of the following: '1', 'true', 't', 'yes', 'y', 'on', it will resolve to true
// if source is 'static', value will be false
if (awesomeNewFeature.asBoolean() === true) {
  enableAwesomeNewFeature();
}
```

The API also provides a `getAll` method to read all parameters at once rather than by key:

```js
import { getAll, getRemoteConfig } from '@react-native-firebase/remote-config';

const parameters = getAll(getRemoteConfig());

Object.entries(parameters).forEach($ => {
  const [key, entry] = $;
  console.log('Key: ', key);
  console.log('Source: ', entry.getSource());
  console.log('Value: ', entry.asString());
});
```

### Value source

When a value is read, it contains source data about the parameter. As explained above, if a value is read before it has
been fetched & activated then the value will fallback to the default value set. If you need to validate whether the value
returned from the module was local or remote, the `getSource()` method can be conditionally checked:

```js
import { getRemoteConfig, getValue } from '@react-native-firebase/remote-config';

const awesomeNewFeature = getValue(getRemoteConfig(), 'awesome_new_feature');

if (awesomeNewFeature.getSource() === 'remote') {
  console.log('Parameter value was from the Firebase servers.');
} else if (awesomeNewFeature.getSource() === 'default') {
  console.log('Parameter value was from a default value.');
} else {
  console.log('Parameter value was from a locally cached value.');
}
```

## Caching

Although Remote Config is a data-store, it is not designed for frequent reads - Firebase heavily caches the parameters
(default is 12 hours).

You can however specify your own cache length by specifically calling the `fetch` method with the number of seconds to
cache the values for:

```js
import { fetchConfig, getRemoteConfig } from '@react-native-firebase/remote-config';

// Fetch and cache for 5 minutes
const remoteConfig = getRemoteConfig();
remoteConfig.settings = { minimumFetchIntervalMillis: 300000 };
await fetchConfig(remoteConfig);
```

To bypass caching fully, you can pass a value of `0`. Be warned Firebase may start to reject your requests
if values are requested too frequently.

You can also apply a global cache frequency by calling the `setConfigSettings` method with the `minimumFetchIntervalMillis` property:

```js
import { getRemoteConfig } from '@react-native-firebase/remote-config';

const remoteConfig = getRemoteConfig();
remoteConfig.settings = {
  minimumFetchIntervalMillis: 30000,
};
```

## Real-time updates

Remote Config has the ability to trigger [real-time Remote Config updates](https://firebase.google.com/docs/remote-config/real-time)
in applications that attach one or more listeners for them.

### Key points to know about real-time updates

1. **No activation by default:** If there is a config template update and you have a listener subscribed for real time updates, the native SDK will automatically fetch the new remote config template _but will not activate it for you_. Your callback code is responsible for activating the new template, and your application code is responsible for getting and reacting to the new config template values
1. **Keys considered updated until activated:** Config template keys are considered updated _if they have been changed since you last **activated** the remote config template_. If you never activate the new template, previously changed keys will continue to show up in the set of updated keys sent to your registered listener callback
1. **Real-time updates has a cost:** If you attach a listener, the native SDK opens a persistent web socket to the firebase servers. If all listeners are unsubscribed, this web socket is closed. Consider the battery usage and network data usage implications for your users
1. **_Unactivated changes result in immediate callback_** If there has been a template change since you last activated, and you attach a listener, that listener will be called _immediately_ to update you on the pending changes

### Known Issues

1. **_Other platform requires a `fetchAndActivate` before updates come through_** During testing here in react-native-firebase and the sister project `FlutterFire`, we noticed that the firebase-js-sdk does not seem to send realtime update events through unless you have previously called `fetchAndActivate`

Here is an example of how to use the feature, with comments emphasizing the key points to know:

```js
import { activate, getRemoteConfig, onConfigUpdate } from '@react-native-firebase/remote-config';

// Add a config update listener where appropriate, perhaps in app startup, or a specific app area.
// Multiple listeners are supported, so listeners may be screen-specific and only handle certain keys
// depending on application requirements
let remoteConfigListenerUnsubscriber = onConfigUpdate(getRemoteConfig(), {
  next: update => {
    console.log('remote-config keys updated: ' + Array.from(update.getUpdatedKeys()));

    // If you use realtime updates, the SDK fetches the new config for you.
    // However, you must activate the new config so it is in effect
    activate(getRemoteConfig());
  },
  error: error => {
    console.log('remote-config listener subscription error: ' + error);
  },
});

// unsubscribe the listener when no longer needed - remote config will close the network socket if there
// are no active listeners, potentially minimizing application user data and battery usage.
remoteConfigListenerUnsubscriber();
```
```

### Cloud Storage

Source: https://rnfirebase.io/storage/usage

```mdx

# Installation

This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app" module, view the
[Getting Started](/) documentation.

```bash
# Install & setup the app module
yarn add @react-native-firebase/app

# Install the storage module
yarn add @react-native-firebase/storage

# If you're developing your app using iOS, run this command
cd ios/ && pod install
```

If you're using an older version of React Native without autolinking support, or wish to integrate into an existing project,
you can follow the manual installation steps for [iOS](/storage/usage/installation/ios) and [Android](/storage/usage/installation/android).

# Platform support and New Architecture

|                      |                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------- |
| **Platforms**        | Android, iOS (native Firebase SDK)                                                             |
| **New Architecture** | **Required** from v26. See [Migrating to v26](/migrating-to-v26#new-architecture-requirement). |

**Platform notes:** `putFile` and `writeToFile` are **native-only** file-path APIs with no firebase-js-sdk equivalent. Use `FilePath` from `@react-native-firebase/app` for device paths.

# What does it do

Storage is built for app developers who need to store and serve user-generated content, such as photos or videos.

<YouTube id="_tyjqozrEPY" />

Your data is stored in a Google Cloud Storage bucket, an exabyte scale object storage solution with high availability and
global redundancy. Storage lets you securely upload these files directly from mobile devices, handling spotty networks with ease.

# Usage

Your files are stored in a Google Cloud Storage bucket. The files in this bucket are presented in a hierarchical structure,
just like a file system. By creating a reference to a file, your app gains access to it. These references can then be
used to upload or download data, get or update metadata or delete the file. A reference can either point to a specific
file or to a higher level node in the hierarchy.

The Storage module also provides support for multiple buckets.

You can view your buckets on the [Firebase Console](https://console.firebase.google.com/project/_/storage/files).

## Creating a reference

A reference is a local pointer to some file on your bucket. This can either be a file which already exists, or one
which does not exist yet. To create a reference, use the `ref` method:

```js
import { getStorage, ref } from '@react-native-firebase/storage';

const reference = ref(getStorage(), 'black-t-shirt-sm.png');
```

You can also specify a file located in a deeply nested directory:

```js
const reference = ref(getStorage(), '/images/t-shirts/black-t-shirt-sm.png');
```

## Upload a file

To upload a file directly from the users device, the `putFile` method on a reference accepts a string path to the file
on the users device. For example, you may be creating an app which uploads users photos. The React Native Firebase
library provides [Utils](/app/utils) to help identify device directories:

```jsx
import React, { useEffect } from 'react';
import { View, Button } from 'react-native';

import { utils } from '@react-native-firebase/app';
import { getStorage, ref, putFile } from '@react-native-firebase/storage';

function App() {
  const storage = getStorage();
  const reference = ref(storage, 'black-t-shirt-sm.png');

  return (
    <View>
      <Button
        onPress={async () => {
          const pathToFile = `${utils.FilePath.PICTURES_DIRECTORY}/black-t-shirt-sm.png`;
          await putFile(reference, pathToFile);
        }}
      />
    </View>
  );
}
```

### Tasks

The `putFile` method returns a [`Task`](https://invertase.github.io/react-native-firebase/_react-native-firebase/storage/FirebaseStorageTypes/Task.html), which if required, allows you to hook into information
such as the current upload progress:

```js
const task = putFile(reference, pathToFile);

task.on('state_changed', taskSnapshot => {
  console.log(`${taskSnapshot.bytesTransferred} transferred out of ${taskSnapshot.totalBytes}`);
});

task.then(() => {
  console.log('Image uploaded to the bucket!');
});
```

A task also provides the ability to pause & resume on-going operations:

```js
const task = putFile(reference, pathToFile);

task.pause();

// Sometime later...
task.resume();
```

## Download URLs

A common use-case for Cloud Storage is to use it as a global Content Delivery Network (CDN) for your images. When uploading
files to a bucket, they are not automatically available for consumption via a HTTP URL. To generate a new Download URL, you
need to call the `getDownloadURL` method on a reference:

```js
import { getStorage, ref, getDownloadURL } from '@react-native-firebase/storage';

const url = await getDownloadURL(ref(getStorage(), 'images/profile-1.png'));
```

> Images uploaded manually via the Firebase Console automatically generate a download URL.

## Listing files & directories

If you wish to view a full list of the current files & directories within a particular bucket reference, you can use
the `list` method. The results are however paginated, and if more results are available you can pass a page token into the request:

```js
import { getStorage, ref } from '@react-native-firebase/storage';

function listFilesAndDirectories(reference, pageToken) {
  return reference.list({ pageToken }).then(result => {
    // Loop over each item
    result.items.forEach(ref => {
      console.log(ref.fullPath);
    });

    if (result.nextPageToken) {
      return listFilesAndDirectories(reference, result.nextPageToken);
    }

    return Promise.resolve();
  });
}

const reference = ref(getStorage(), 'images');

listFilesAndDirectories(reference).then(() => {
  console.log('Finished listing');
});
```

## Security

By default your bucket will come with rules which allows only authenticated users on your project to access it. You can
however fully customize the security rules to your own applications requirements.

To learn more, view the [Storage Security](https://firebase.google.com/docs/storage/security/start) documentation
on the Firebase website.

## Multiple Buckets

A single Firebase project can have multiple storage buckets. The module will use the default bucket if no bucket argument
is passed to the `storage` instance. To switch buckets, provide the module with the `gs://` bucket URL found on the
Firebase Console, under Storage > Files.

```js
import { getStorage } from '@react-native-firebase/storage';
import { getApp } from '@react-native-firebase/app';

const defaultStorageBucket = getStorage();
const secondaryStorageBucket = getStorage(getApp(), 'gs://my-secondary-bucket.appspot.com');
```
```

### VertexAI

Source: https://rnfirebase.io/vertexai/usage

```mdx

Vertex AI has been deprecated by Google in favor of the Firebase AI Logic SDK.

Using the Firebase AI Logic SDK with the Vertex AI Gemini API is still generally available (GA).

To start using the new SDK, import the `@react-native-firebase/ai` package and use the modular method `getAI()` to initialize. See details in the [migration guide](https://firebase.google.com/docs/vertex-ai/migrate-to-latest-sdk).

```javascript
// BEFORE - using firebase/vertexai
import { initializeApp } from 'firebase/app';
import { getVertexAI, getGenerativeModel } from 'firebase/vertexai'; // Remove this

// AFTER - using firebase/ai
import { initializeApp } from 'firebase/app';
import { getAI, getGenerativeModel } from 'firebase/ai'; // Add this
```

# Platform support

|                      |                                                                        |
| -------------------- | ---------------------------------------------------------------------- |
| **Platforms**        | Android, iOS, Web (firebase-js-sdk interop)                            |
| **New Architecture** | **Not required** — pure JavaScript package with no native TurboModule. |

> **Deprecated:** Prefer `@react-native-firebase/ai` — see the [Firebase migration guide](https://firebase.google.com/docs/vertex-ai/migrate-to-latest-sdk).
```

### currentDocument

Source: https://rnfirebase.io/firestore/pipelines/current-document

```mdx

`currentDocument()` returns an [expression](/firestore/pipelines) representing the Firestore document row the pipeline is processing. Use it when you need the full document map in a projection or when binding the document to a variable in upstream firebase-js-sdk samples.

Import from the pipelines entry point:

```js
import { currentDocument, mapGet } from '@react-native-firebase/firestore/pipelines';
```

# Basic example

Read a field from the current document map with `mapGet`:

```js
import { getFirestore } from '@react-native-firebase/firestore';
import {
  currentDocument,
  execute,
  field,
  mapGet,
} from '@react-native-firebase/firestore/pipelines';

const db = getFirestore('your-enterprise-database-id');

const snapshot = await execute(
  db
    .pipeline()
    .collection('books')
    .select(mapGet(currentDocument(), 'title').as('title'), field('author').as('author')),
);

snapshot.results.forEach(row => {
  console.log(row.data().title, row.data().author);
});
```

# Upstream define-stage pattern

The [firebase-js-sdk](https://firebase.google.com/docs/reference/js/firestore_pipelines#currentdocument) also shows binding the document with a `define` stage:

```js
firestore
  .pipeline()
  .collection('books')
  .define(currentDocument().as('doc'))
  .select(variable('doc').mapGet('title'));
```

React Native Firebase does not expose the `define` stage yet. Until it does, prefer `mapGet(currentDocument(), 'fieldName')` or other helpers that accept expression arguments directly.

# Platform support

| Platform | Support                                 |
| -------- | --------------------------------------- |
| Android  | Supported (native Firestore SDK)        |
| iOS      | Supported (native Firestore SDK)        |
| macOS    | Supported (firebase-js-sdk web interop) |

Pipeline execution requires a Firestore **Enterprise** database. See [Pipelines overview](/firestore/pipelines).
```

### ifNull

Source: https://rnfirebase.io/firestore/pipelines/if-null

```mdx

`ifNull()` returns a fallback when the first argument evaluates to **null** or is **absent**. Unlike [`ifAbsent()`](/firestore/pipelines), which only substitutes missing fields, `ifNull()` also replaces explicit `null` values stored on the document.

Import from the pipelines entry point:

```js
import { ifNull, field, constant } from '@react-native-firebase/firestore/pipelines';
```

# Basic example

```js
import { getFirestore } from '@react-native-firebase/firestore';
import { execute, field, constant, ifNull } from '@react-native-firebase/firestore/pipelines';

const db = getFirestore('your-enterprise-database-id');

const snapshot = await execute(
  db
    .pipeline()
    .collection('users')
    .select(ifNull(field('displayName'), constant('Anonymous')).as('displayName')),
);

snapshot.results.forEach(row => {
  console.log(row.data().displayName);
});
```

# Fluent form

```js
field('displayName').ifNull(constant('Anonymous')).as('displayName');
```

# Comparison with ifAbsent

| Helper     | Missing field | Explicit `null` on document |
| ---------- | ------------- | --------------------------- |
| `ifAbsent` | fallback      | keeps `null`                |
| `ifNull`   | fallback      | fallback                    |

Use `ifNull` when you want one expression to cover both absent and null values.

# Upstream reference

See the [firebase-js-sdk `ifNull` declaration](https://firebase.google.com/docs/reference/js/firestore_pipelines#ifnull) for full overload signatures.
```

### Pipeline SDK compatibility

Source: https://rnfirebase.io/firestore/pipelines/sdk-compatibility

```mdx

React Native Firebase aims to be a **drop-in replacement** for the [firebase-js-sdk Firestore pipelines module](https://firebase.google.com/docs/reference/js/firestore_pipelines) on React Native targets. Most expression helpers, stage builders, and types match the JS SDK. This page summarizes deliberate gaps so you can plan migrations from web samples.

CI enforces parity through `yarn compare:types` and the `firestore-pipelines` allowlist in the repository. When a gap closes, the allowlist entry is removed and this page should be updated.

# Summary

| Category                                                                    | Status                                   |
| --------------------------------------------------------------------------- | ---------------------------------------- |
| Core pipeline builder (`pipeline()`, stages, `execute()`)                   | **Supported** on Android, iOS, and macOS |
| Expression helpers (aggregates, strings, arrays, maps, timestamps, vectors) | **Supported** on Android, iOS, and macOS |
| Current firebase-js-sdk pipeline exports                                    | **Supported** through compare-types      |

# Not yet available in React Native Firebase

All pipeline exports currently tracked in the compare-types allowlist are supported. Check release notes when upgrading for newly added firebase-js-sdk pipeline APIs.

## Platform notes

Compare-types parity does not guarantee identical runtime behavior on every target. The `parent()` expression helper may be unavailable on web and on macOS when pipelines execute through the firebase-js-sdk interop path (**P-036**). Pipelines that use the search stage with text matching need a deployed composite search index on the Enterprise `pipelines-e2e` database used in CI (**P-035**); native iOS and Android e2e cover search after that index is deployed. The `geoDistance` helper is covered for types and serialization; dedicated runtime e2e is deferred because native platforms lower it through the generic function path. Maintainer drift IDs and e2e expectations are tracked in [`okf-bundle/packages/firestore/pipeline-platform-parity.md`](https://github.com/invertase/react-native-firebase/blob/main/okf-bundle/packages/firestore/pipeline-platform-parity.md).

If you depend on one of these, follow [Firebase pipeline release notes](https://firebase.google.com/support/release-notes/js) and React Native Firebase changelogs, or open a feature request with your use case.

# Type-shape parity

The current `firestore-pipelines` compare-types config has no `differentShape` entries. `StageOptions`, `TimeGranularity`, `isType`, and `timestampDiff` now match the firebase-js-sdk declarations, including the lowercase `isoweek` and `isoyear` time granularity literals.

# Platform execution matrix

| Platform | Execution backend               | Pipeline database  |
| -------- | ------------------------------- | ------------------ |
| Android  | Native Android Firestore SDK    | Enterprise (cloud) |
| iOS      | Native iOS Firestore SDK        | Enterprise (cloud) |
| macOS    | firebase-js-sdk via web interop | Enterprise (cloud) |

All platforms require network access to your Firestore Enterprise database for pipeline `execute()`. The local Firestore emulator is not a supported target for pipeline development today.

# Staying up to date

- Upstream API reference: [firebase-js-sdk `firestore/pipelines`](https://firebase.google.com/docs/reference/js/firestore_pipelines)
- RNFB import path: `@react-native-firebase/firestore/pipelines`
- Repository parity config: `.github/scripts/compare-types/configs/firestore-pipelines.ts`

When a row in the tables above is resolved in a release, it will be removed from the compare-types allowlist. Check release notes and this page after upgrading `@react-native-firebase/firestore`.
```

### subcollection

Source: https://rnfirebase.io/firestore/pipelines/subcollection

```mdx

`subcollection()` creates a **detached pipeline** aimed at a subcollection path relative to each parent document in the outer pipeline. The returned pipeline has no database instance and cannot be executed directly. Embed it in another pipeline with `toScalarExpression()` or `toArrayExpression()`.

Import from the pipelines entry point:

```js
import {
  subcollection,
  field,
  countAll,
  average,
} from '@react-native-firebase/firestore/pipelines';
```

# Basic example

```js
import { getFirestore } from '@react-native-firebase/firestore';
import {
  execute,
  field,
  subcollection,
  countAll,
  average,
} from '@react-native-firebase/firestore/pipelines';

const db = getFirestore('your-enterprise-database-id');

const snapshot = await execute(
  db
    .pipeline()
    .collection('restaurants')
    .addFields(
      subcollection('reviews')
        .aggregate(countAll().as('reviewCount'), average('rating').as('avgRating'))
        .toScalarExpression()
        .as('reviewSummary'),
    )
    .select('name', field('reviewSummary')),
);

snapshot.results.forEach(row => {
  const summary = row.data().reviewSummary;
  console.log(row.data().name, summary.reviewCount, summary.avgRating);
});
```

# Options object

Pass a `SubcollectionStageOptions` object when you need raw stage options:

```js
subcollection({ path: 'reviews', rawOptions: {} });
```

# Related helpers

| Helper                      | Use when                                           |
| --------------------------- | -------------------------------------------------- |
| `subcollection`             | Target a subcollection relative to each parent doc |
| `toScalarExpression()`      | Embed a nested pipeline as a single scalar field   |
| `toArrayExpression()`       | Embed a nested pipeline as an array of documents   |
| `SubcollectionStageOptions` | Options type for the subcollection source stage    |

# Upstream reference

See the [firebase-js-sdk `subcollection` declaration](https://firebase.google.com/docs/reference/js/firestore_pipelines#subcollection) for overload signatures and nested pipeline usage.
```

### switchOn

Source: https://rnfirebase.io/firestore/pipelines/switch-on

```mdx

`switchOn()` behaves like a `switch` statement: pass alternating **condition / result** pairs, plus an optional default result as the final argument when no condition matches.

Import from the pipelines entry point:

```js
import { switchOn, field, constant, equal } from '@react-native-firebase/firestore/pipelines';
```

# Basic example

```js
import { getFirestore } from '@react-native-firebase/firestore';
import {
  execute,
  field,
  constant,
  equal,
  switchOn,
} from '@react-native-firebase/firestore/pipelines';

const db = getFirestore('your-enterprise-database-id');

const snapshot = await execute(
  db
    .pipeline()
    .collection('orders')
    .select(
      switchOn(
        equal(field('status'), constant(1)),
        constant('Active'),
        equal(field('status'), constant(2)),
        constant('Pending'),
        constant('Unknown'),
      ).as('statusLabel'),
    ),
);

snapshot.results.forEach(row => {
  console.log(row.data().statusLabel);
});
```

# Comparison with conditional

| Helper        | Use when                                          |
| ------------- | ------------------------------------------------- |
| `conditional` | One boolean test with explicit then/else branches |
| `switchOn`    | Multiple discrete cases plus optional default     |

On **iOS**, the native pipeline runtime evaluates `switchOn` on Firebase iOS SDK **12.12.0+** (RNFB pins **12.15.0**); unified cross-platform e2e.

# Upstream reference

See the [firebase-js-sdk `switchOn` declaration](https://firebase.google.com/docs/reference/js/firestore_pipelines#switchon) for full overload signatures.
```

### timestampDiff

Source: https://rnfirebase.io/firestore/pipelines/timestamp-diff

```mdx

`timestampDiff()` calculates how much time separates an **end** timestamp from a **start** timestamp, expressed in the unit you choose (`day`, `hour`, `minute`, and so on).

Import from the pipelines entry point:

```js
import { timestampDiff, field } from '@react-native-firebase/firestore/pipelines';
```

# Basic example

```js
import { getFirestore } from '@react-native-firebase/firestore';
import { execute, field, timestampDiff } from '@react-native-firebase/firestore/pipelines';

const db = getFirestore('your-enterprise-database-id');

const snapshot = await execute(
  db
    .pipeline()
    .collection('events')
    .select(
      timestampDiff(field('endTime'), field('startTime'), 'day').as('daysApart'),
      timestampDiff('endTime', 'startTime', 'hour').as('hoursApart'),
    ),
);

snapshot.results.forEach(row => {
  console.log(row.data().daysApart, row.data().hoursApart);
});
```

# Related helpers

| Helper              | Use when                                         |
| ------------------- | ------------------------------------------------ |
| `timestampDiff`     | Measure elapsed time between two timestamps      |
| `timestampAdd`      | Shift a timestamp forward by an amount           |
| `timestampSubtract` | Shift a timestamp backward by an amount          |
| `TimeUnit`          | Type for `'microsecond'` … `'day'` unit literals |

# Upstream reference

See the [firebase-js-sdk `timestampDiff` declaration](https://firebase.google.com/docs/reference/js/firestore_pipelines#timestampdiff) for full overload signatures.
```

### timestampExtract

Source: https://rnfirebase.io/firestore/pipelines/timestamp-extract

```mdx

`timestampExtract()` reads a calendar component—such as `year`, `month`, or `day`—from a timestamp. An optional timezone shifts the calendar used for extraction.

Import from the pipelines entry point:

```js
import { timestampExtract, field } from '@react-native-firebase/firestore/pipelines';
```

# Basic example

```js
import { getFirestore } from '@react-native-firebase/firestore';
import { execute, field, timestampExtract } from '@react-native-firebase/firestore/pipelines';

const db = getFirestore('your-enterprise-database-id');

const snapshot = await execute(
  db
    .pipeline()
    .collection('events')
    .select(
      timestampExtract(field('createdAt'), 'year').as('createdYear'),
      timestampExtract('createdAt', 'month').as('createdMonth'),
      field('createdAt').timestampExtract('day').as('createdDay'),
    ),
);

snapshot.results.forEach(row => {
  console.log(row.data().createdYear, row.data().createdMonth, row.data().createdDay);
});
```

# Related helpers

| Helper              | Use when                                      |
| ------------------- | --------------------------------------------- |
| `timestampExtract`  | Read year, month, day, or other calendar part |
| `timestampTruncate` | Bucket timestamps to a granularity            |
| `timestampDiff`     | Measure elapsed time between timestamps       |
| `TimePart`          | Type for part literals such as `'year'`       |

# Upstream reference

See the [firebase-js-sdk `timestampExtract` declaration](https://firebase.google.com/docs/reference/js/firestore_pipelines#timestampextract) for full overload signatures and supported `TimePart` values.
```

### iOS Messaging Setup

Source: https://rnfirebase.io/messaging/usage/ios-setup

```mdx

Integrating the Cloud Messaging module on iOS devices requires additional setup before your devices receive messages.
There are also a number of prerequisites which are required to enable messaging:

- You must have an active [Apple Developer Account](https://developer.apple.com/membercenter/index.action).
- A **physical iOS device** is preferred for end-to-end FCM/APNs registration and message delivery.
  - Firebase Cloud Messaging integrates with the [Apple Push Notification service (APNs)](https://developer.apple.com/notifications/).
  - On **ARM64 iOS Simulator**, React Native Firebase skips UIKit `registerForRemoteNotifications`
    (calling it can wedge the main thread). `registerDeviceForRemoteMessages` may then reject with
    `messaging/registration-timeout` and will not yield a real APNs token — see
    [Auto Registration (iOS)](/messaging/usage#auto-registration-ios) and
    [Migrating to v26 — iOS APNs registration](/migrating-to-v26#ios-apns-registration-arm64-simulator--new-promise-rejections).

## Configuring your app

Before your application can start to receive messages, you must explicitly enable "Push Notifications" and "Background Modes"
within Xcode.

Open your project's workspace file via Xcode (found within the `/ios` directory). The file name is prefixed with your project name,
for example `/ios/myapp.xcworkspace`. Once open, follow the steps below:

1. Select your project.
2. Select the project target.
3. Select the "Signing & Capabilities" tab.

![Example with Steps](https://images.prismic.io/invertase/c954c8ed-a6bf-42f3-9b1d-c9eac937f9ec_xcode-signing-tab.png?auto=format)

### Enable Push Notifications

Next the "Push Notifications" capability needs to be added to the project. This can be done via the "Capability" option on the
"Signing & Capabilities" tab:

1. Click on the "+ Capabilities" button.
2. Search for "Push Notifications".

![Enabling the Push Notification capability](https://images.prismic.io/invertase/d682a40c-07ab-4fce-90a7-fb4278643323_xcode-enable-push-notification.png?auto=format)

Once selected, the capability will be shown below the other enabled capabilities. If no option appears when searching, the
capability may already be enabled.

### Enable Background Modes

Next the "Background Modes" capability needs to be enabled, along with both the "Background fetch" and "Remote notifications" sub-modes.
This can be added via the "Capability" option on the "Signing & Capabilities" tab:

1. Click on the "+ Capabilities" button.
2. Search for "Background Modes".

![Enabling the Background Modes capability](https://images.prismic.io/invertase/517e18ad-37a7-4f44-a89e-c5947ea3742e_xcode-enable-background-modes-capability.png?auto=compress,format)

Once selected, the capability will be shown below the other enabled capabilities. If no option appears when searching, the
capability may already be enabled.

Now ensure that both the "Background fetch" and the "Remote notifications" sub-modes are enabled:

![Enabling the sub-modes](https://images.prismic.io/invertase/3a618574-dd9f-4478-9f39-9834d142b2e5_xcode-background-modes-check.gif?auto=compress,format)

## Linking APNs with FCM (iOS)

> Note: APNs is now required for both `foreground` and `background` messaging to function correctly on iOS.

A few steps are required:

1. [Registering a key](/messaging/usage/ios-setup#1-registering-a-key).
2. [Registering an App Identifier](/messaging/usage/ios-setup#2-registering-an-app-identifier).
3. [Generating a provisioning profile](/messaging/usage/ios-setup#3-generating-a-provisioning-profile).

All of these steps require you to have access to your [Apple Developer](https://developer.apple.com/membercenter/index.action) account.
Once on the account, navigate to the [Certificates, Identifiers & Profiles](https://developer.apple.com/account/resources/certificates/list)
tab on the account sidebar:

![Certificates, Identifiers & Profiles menu item](https://images.prismic.io/invertase/c0a795c8-ebca-41c3-9a8d-23f09deb625f_apple-dev-tab.png?auto=format)

### 1. Registering a key

A key can be generated which gives the FCM full access over the Apple Push Notification service (APNs). On the "Keys" menu item,
register a new key. The name of the key can be anything, however you must ensure the APNs service
is enabled:

![Enable "Apple Push Notification (APNs)"](https://images.prismic.io/invertase/01fefe19-132f-4b88-8c17-9dc40357e4ce_apple-key.png?auto=format)

Click "Continue" & then "Save". Once saved, you will be presented with a screen displaying the private "Key ID" & the ability
to download the key. Copy the ID, and download the file to your local machine:

![Copy Key ID & Download File](https://images.prismic.io/invertase/2c7f194c-10a9-4011-8f80-78b8fc521af8_app-key-final.png?auto=format)

The file & Key ID can now be added to your Firebase Project. On the [Firebase Console](https://console.firebase.google.com/project/_/settings/cloudmessaging),
navigate to the "Project settings" and select the "Cloud Messaging" tab. Select your iOS application under the "iOS app configuration" heading.

Upload the downloaded file and enter the Key ID:

![Upload the key & Key ID](https://images.prismic.io/invertase/74bd1df4-c9e9-465c-9e0f-cacf6e26d68c_7539b8ec-c310-40dd-91e5-69f19009786f_apple-fcm-upload-key.gif?auto=compress,format)

### 2. Registering an App Identifier

For messaging to work when your app is built for production, you must create a new App Identifier which is linked to the
application that you're developing.

On the "Identifiers" menu item, register a App Identifier. Select the "App IDs" option and click "Continue".

The following screen enables you to link the identifier to your application via the "Bundle ID". This is a unique string
which was generated when starting your new React Native project. Your Bundle ID can be obtained within Xcode, under the
"General" tab for your project target:

![Project Bundle ID](https://images.prismic.io/invertase/7108ff7f-ce94-4452-851d-fa5dde668a9a_xcode-bundle-id.png?auto=compress,format)

Next, follow these steps:

1. Enter a description for the identifier.
2. Enter the "Bundle ID" copied from Xcode.
3. Scroll down and enable the "Push Notifications" capability (along with any others your app uses).

![Create an identifier](https://images.prismic.io/invertase/0e711691-ccd2-43ab-9c0c-7696b6790153_apple-identifier.gif?auto=format)

Save the identifier, it'll be used when creating a provisioning profile in the next step.

### 3. Generating a provisioning profile

A provisioning profile enables signed communicate between Apple and your application. Since messaging can only be used on
real devices, a signed certificate ensures that the app being installed on a device is genuine and has the correct
permissions enabled.

On the "Profiles" menu item, register a new Profile. Select the "iOS App Development" checkbox and click "Continue".

If you followed [Step 2](/messaging/usage/ios-setup#2-registering-an-app-identifier) correctly, your App Identifier will be available in the drop down
provided:

![Select the App Identifier](https://images.prismic.io/invertase/9fd060fa-4afa-4dfe-8eaa-4b1156cdd912_apple-select-app-id.png?auto=format)

Click "Continue". On the next screen you will be presented with the Certificates on your Apple account. Select the user
certificates that you wish to assign this provisioning profile too. If you have not yet created a Certificate, you must set
one up on your account.

To create a new Certificate, follow the [Apple documentation](https://help.apple.com/developer-account/#/devbfa00fef7). Once
the Certificate has been downloaded, upload it to the Apple Developer console via the "Certificates" menu item.

The created provisioning profile can now be used when building your application (in both debug and release mode) onto a
real device (using Xcode). Back within Xcode, select your project target and select the "Signing & Capabilities" tab.
If Xcode (via Preferences) is linked to your Apple Account, Xcode can automatically sync the profile created above. Otherwise,
you must manually add the profile from the Apple Developer console:

1. Select the project.
2. Select the project target.
3. Assign the provisioning profile.

![Assign the provisioning profile via Xcode](https://images.prismic.io/invertase/50349f49-19a0-45f4-b899-e6bc3015c509_xcode-assign-profile.png?auto=format)

## Next steps

Once the above has been completed, you're ready to get started receiving messages on your iOS device for both
testing and production. To rebuild your app, run the following command:

```bash
npx react-native run-ios
```
```

### Messaging with XMPP

Source: https://rnfirebase.io/messaging/usage/messaging-with-xmpp

```mdx

## Introduction

This is a reference for using the Firebase Messaging service to send and receive messages directly between devices. Although methods (e.g. `sendMessage()`) are provided for sending and receiving message, additional configuration is needed to ensure a working solution for direct messaging from devices.

> A custom solution is **only required** if plan to exchange messages directly between devices (including the message sender device)
> please ensure a solution from this article has been configured to successfully send and receive messages from a device.

The following describes how to set up a server to handle messages, including...

- A custom XMPP server with XCS for receiving messages.
- Firebase admin for sending messages.

So what are we going to do?

1. Send a message from a client device.
2. Intercept using a custom XMPP server.
3. Push the message to Firebase using firebase-admin
4. Receive the message and acknowledge on other connected devices.

## Why can't I send and receive messages?

A common instance involves an implementation similar to the following.

```js
import {
  getMessaging,
  onMessage,
  onMessageSent,
  onSendError,
  sendMessage,
} from '@react-native-firebase/messaging';

const messaging = getMessaging();

onMessage(messaging, message => {
  console.log('Received a message');
});

onMessageSent(messaging, message => {
  console.log('Sent a message');
});

onSendError(messaging, message => {
  console.log('Received an Error');
});

sendMessage(messaging, {
  data: {
    foo: 'bar',
  },
});
```

Although correct, none of the listeners will acknowledge a `message` or `error`.

Permissions are limited on the client meaning an additional solution is required to communicate between `FCM` and any connected devices.

## How do I receive messages?

This is where you will need to deploy a custom server based, for example one based on Node XCS

Below is an example of how to configure a custom XMPP server using `node-xcs`.

```js
const Sender = require('node-xcs').Sender;

async function operation() {
  return () => {
    console.log('Listening >>>');

    // Enter firebase credentials here. {SenderID, ServerKey}
    var xcs = new Sender('XXXXXXXX', 'XXXXXXXX');

    xcs.start();

    xcs.on('message', function (messageId, from, data, category) {
      console.log('received message', messageId, from, data, category);
    });

    xcs.on('receipt', function (messageId, from, data, category) {
      console.log('received receipt', arguments);
    });

    xcs.on('error', e => console.warn('XMPP error.', e));
  };
}

async function app() {
  await operation();
}

app();
```

## How do I send messages?

For sending messages we can use the Firebase Admin SDK.

```js
const admin = require('firebase-admin');
const serviceAccount = require('./service-account.json');

(async () => {
  admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    databaseURL: 'XXXXXXXX',
  });

  await admin.messaging().send({
    token: 'XXXXXXX',
    data: {
      foo: 'bar',
    },
  });
})();
```
```

### Android Installation

Source: https://rnfirebase.io/analytics/usage/installation/android

```mdx

# Android Manual Installation

The following steps are only required if you are using React Native 0.59 or earlier or need to manually integrate the library.

## 1. Update Gradle Settings

Add the following to your projects `/android/settings.gradle` file:

```groovy
include ':@react-native-firebase_analytics'
project(':@react-native-firebase_analytics').projectDir = new File(rootProject.projectDir, './../node_modules/@react-native-firebase/analytics/android')
```

## 2. Update Gradle Dependencies

Add the React Native Firebase module dependency to your `/android/app/build.gradle` file:

```groovy
dependencies {
  // ...
  implementation project(path: ":@react-native-firebase_analytics")
}
```

## 3. Add package to the Android Application

Import and apply the React Native Firebase module package to your `/android/app/src/main/java/**/MainApplication.java` file:

```java
import io.invertase.firebase.analytics.ReactNativeFirebaseAnalyticsPackage;
```

Add the package to the registry:

```java
protected List<ReactPackage> getPackages() {
  return Arrays.asList(
    new MainReactPackage(),
    new ReactNativeFirebaseAnalyticsPackage(),
```

## 4. Rebuild the project

Once the above steps have been completed, rebuild your Android project:

```bash
npx react-native run-android
```
```

### iOS Installation

Source: https://rnfirebase.io/analytics/usage/installation/ios

```mdx

# iOS Manual Installation

The following steps are only required if you are using React Native 0.59 or earlier or need to manually integrate the library.

## 1. Add the Pod

Add the `RNFBAnalytics` Pod to your projects `/ios/Podfile`:

```ruby
target 'app' do
  # ...
  pod 'RNFBAnalytics', :path => '../node_modules/@react-native-firebase/analytics'
end
```

## 2. Update Pods & rebuild the project

You may need to update your local Pods in order for the `RNFBAnalytics` Pod to be installed in your project:

```bash
cd ios/
pod install --repo-update
```

Once the Pods have installed locally, rebuild your iOS project:

```bash
npx react-native run-ios
```
```

### Android Installation

Source: https://rnfirebase.io/auth/usage/installation/android

```mdx

# Android Manual Installation

The following steps are only required if you are using React Native 0.59 or earlier or need to manually integrate the library.

## 1. Update Gradle Settings

Add the following to your projects `/android/settings.gradle` file:

```groovy
include ':@react-native-firebase_auth'
project(':@react-native-firebase_auth').projectDir = new File(rootProject.projectDir, './../node_modules/@react-native-firebase/auth/android')
```

## 2. Update Gradle Dependencies

Add the React Native Firebase module dependency to your `/android/app/build.gradle` file:

```groovy
dependencies {
  // ...
  implementation project(path: ":@react-native-firebase_auth")
}
```

## 3. Add package to the Android Application

Import and apply the React Native Firebase module package to your `/android/app/src/main/java/**/MainApplication.java` file:

Import the package:

```java
import io.invertase.firebase.auth.ReactNativeFirebaseAuthPackage;
```

Add the package to the registry:

```java
protected List<ReactPackage> getPackages() {
  return Arrays.asList(
    new MainReactPackage(),
    new ReactNativeFirebaseAuthPackage(),
```

## 4. Rebuild the project

Once the above steps have been completed, rebuild your Android project:

```bash
npx react-native run-android
```
```

### iOS Installation

Source: https://rnfirebase.io/auth/usage/installation/ios

```mdx

# iOS Manual Installation

The following steps are only required if you are using React Native 0.59 or earlier or need to manually integrate the library.

## 1. Add the Pod

Add the `RNFBAuth` Pod to your projects `/ios/Podfile`:

```ruby
target 'app' do
  #  ...
  pod 'RNFBAuth', :path => '../node_modules/@react-native-firebase/auth'
end
```

## 2. Update Pods & rebuild the project

You may need to update your local Pods in order for the `RNFBAuth` Pod to be installed in your project:

```bash
$ cd ios/
$ pod install --repo-update
```

Once the Pods have installed locally, rebuild your iOS project:

```bash
npx react-native run-ios
```
```

### Android Installation

Source: https://rnfirebase.io/crashlytics/usage/installation/android

```mdx

# Android Installation

The following steps are only required if you are using React Native 0.59 or earlier or need to manually integrate the library.

## 1. Update Gradle Settings

Add the following to your projects `/android/settings.gradle` file:

```groovy
include ':@react-native-firebase_crashlytics'
project(':@react-native-firebase_crashlytics').projectDir = new File(rootProject.projectDir, './../node_modules/@react-native-firebase/crashlytics/android')
```

## 2. Update Gradle Dependencies

Add the React Native Firebase Crashlytics module dependency to your `/android/app/build.gradle` file:

```groovy
dependencies {
  // ...
  implementation project(path: ":@react-native-firebase_crashlytics")
}
```

## 3. Add package to the Android Application

Import and apply the React Native Firebase module package to your `/android/app/src/main/java/**/MainApplication.java` file:

Import the package:

```java
import io.invertase.firebase.crashlytics.ReactNativeFirebaseCrashlyticsPackage;
```

Add the package to the registry:

```java
protected List<ReactPackage> getPackages() {
  return Arrays.asList(
    new MainReactPackage(),
    new ReactNativeFirebaseCrashlyticsPackage(),
```

## 4. Additional Android Setup

Android requires additional steps in order to complete setup. View the [Android Setup](/crashlytics/android-setup) documentation
for more information.

## 5. Rebuild the project

Once the above steps have been completed, rebuild your Android project:

```bash
npx react-native run-android
```
```

### iOS Installation

Source: https://rnfirebase.io/crashlytics/usage/installation/ios

```mdx

# iOS Manual Installation

The following steps are only required if you are using React Native 0.59 or earlier or need to manually integrate the library.

## 1. Add the Pod

Add the `RNFBCrashlytics` Pod to your projects `/ios/Podfile`:

```ruby
target 'app' do
  # Add the RNFBCrashlytics podspec to your app target:
  pod 'RNFBCrashlytics', :path => '../node_modules/@react-native-firebase/crashlytics'
end
```

## 2. Update Pods & rebuild the project

You may need to update your local Pods repository in order for the Pods to be installed in your project:

```bash
$ cd ios/
$ pod install --repo-update
```

Once the Pods have installed locally, rebuild your iOS project:

```bash
npx react-native run-ios
```
```

### Android Setup

Source: https://rnfirebase.io/database/usage/installation/android

```mdx

# Android Manual Linking

The following steps are only required if your environment does not have access to React Native auto-linking.

## 1. Update Gradle Settings

Add the following to your projects `/android/settings.gradle` file:

```groovy
include ':@react-native-firebase_database'
project(':@react-native-firebase_database').projectDir = new File(rootProject.projectDir, './../node_modules/@react-native-firebase/database/android')
```

## 2. Update Gradle Dependencies

Add the React Native Firebase module dependency to your `/android/app/build.gradle` file:

```groovy
// ..
dependencies {
  // ..
  implementation project(path: ":@react-native-firebase_database")
}
```

## 3. Add package to the Android Application

Import and apply the React Native Firebase module package to your `/android/app/src/main/java/**/MainApplication.java` file:

Import the package:

```java
import io.invertase.firebase.database.ReactNativeFirebaseDatabasePackage;
```

Add the package to the registry:

```java
protected List<ReactPackage> getPackages() {
  return Arrays.asList(
    new MainReactPackage(),
    new ReactNativeFirebaseDatabasePackage(),
```

## 4. Rebuild the project

Once the above steps have been completed, rebuild your Android project:

```bash
npx react-native run-android
```
```

### iOS Setup

Source: https://rnfirebase.io/database/usage/installation/ios

```mdx

# iOS Manual Linking

The following steps are only required if you are using React Native 0.59 or earlier or need to manually integrate the library.

## 1. Add the `RNFBAnalytics` Pod

Add the `RNFBDatabase` Pod to your projects `/ios/Podfile`:

```ruby
target 'app' do
  #  ...
  pod 'RNFBDatabase', :path => '../node_modules/@react-native-firebase/database'
end
```

## 2. Update Pods & rebuild the project

You may need to update your local Pods in order for the `RNFBDatabase` Pod to be installed in your project:

```bash
$ cd ios/
$ pod install --repo-update
```

Once the Pods have installed locally, rebuild your iOS project:

```bash
npx react-native run-ios
```
```

### Android Installation

Source: https://rnfirebase.io/firestore/usage/installation/android

```mdx

# Android Manual Installation

The following steps are only required if you are using React Native without auto-linking (0.59 or older) or you need to manually integrate the library.

## 1. Add Firestore to Gradle Settings

Add the following to your projects `/android/settings.gradle` file:

```groovy
include ':@react-native-firebase_firestore'
project(':@react-native-firebase_firestore').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-firebase/firestore/android')
```

## 2. Add Firestore to App Gradle Dependencies

Add the React Native Firebase module dependency to your `/android/app/build.gradle` file:

```groovy
// ..
dependencies {
  // ..
  implementation project(':@react-native-firebase_firestore')
}
```

## 3. Add Firestore to Main Android Application:

Import and apply the React Native Firebase module package to your `/android/app/src/main/java/**/MainApplication.java` file:

```java
import io.invertase.firebase.firestore.ReactNativeFirebaseFirestorePackage;
```

Add the package to the registry:

````java
protected List<ReactPackage> getPackages() {
  return Arrays.asList(
    new MainReactPackage(),
    new ReactNativeFirebaseFirestorePackage(),
```// ..
````

In some scenarios, your Android build may fail with the `app:mergeDexDebug` error. This required that multidex is enabled
for your application. To learn more, read the [Enabling Multidex](/enabling-multidex) documentation.

## 4. Rebuild your project

Once the above steps have been completed, rebuild your Android project:

```bash
npx react-native run-android
```
```

### Cloud Firestore iOS Integration

Source: https://rnfirebase.io/firestore/usage/installation/ios

```mdx

# iOS Manual Linking

The following steps are only required if your environment does not have access to React Native auto-linking (0.59 or older) or you need to manually integrate the library.

## 1. Add the Pod

Add the `RNFBFirestore` Pod to your projects `/ios/Podfile`:

```ruby
target 'app' do
  # ...
  pod 'RNFBFirestore', :path => '../node_modules/@react-native-firebase/firestore'
end
```

## 2. Update Pods & rebuild the project

You may need to update your local Pods in order for the `RNFBFirestore` Pod to be installed in your project:

```bash
$ cd ios/
$ pod install --repo-update
```

Once the Pods have installed locally, rebuild your iOS project:

```bash
npx react-native run-ios
```
```

### Android Setup

Source: https://rnfirebase.io/functions/usage/installation/android

```mdx

# Android Manual Installation

The following steps are only required if your environment does not have access to React Native auto-linking.

## 1. Update Gradle Settings

Add the following to your projects `/android/settings.gradle` file:

```groovy
include ':@react-native-firebase_functions'
project(':@react-native-firebase_functions').projectDir = new File(rootProject.projectDir, './../node_modules/@react-native-firebase/functions/android')
```

## 2. Update Gradle Dependencies

Add the React Native Functions module dependency to your `/android/app/build.gradle` file:

```groovy
dependencies {
  ...
  implementation project(path: ":@react-native-firebase_functions")
}
```

## 3. Add package to the Android Application

Import and apply the React Native Firebase module package to your `/android/app/src/main/java/**/MainApplication.java` file:

Import the package:

```java
import io.invertase.firebase.functions.ReactNativeFirebaseFunctionsPackage;
```

Add the package to the registry:

```java
protected List<ReactPackage> getPackages() {
  return Arrays.asList(
    new MainReactPackage(),
    new ReactNativeFirebaseFunctionsPackage(),
```

## 4. Rebuild the project

Once the above steps have been completed, rebuild your Android project:

```bash
npx react-native run-android
```
```

### iOS Setup

Source: https://rnfirebase.io/functions/usage/installation/ios

```mdx

# iOS Manual Linking

The following steps are only required if your environment does not have access to React Native auto-linking.

## 1. Add the `RNFBFunctions` Pod

Add the `RNFBFunctions` Pod to your projects `/ios/Podfile`:

```ruby
target 'app' do
  # ...
  pod 'RNFBFunctions', :path => '../node_modules/@react-native-firebase/functions'
end
```

## 2. Update Pods & rebuild the project

You may need to update your local Pods in order for the `RNFBFunctions` Pod to be installed in your project:

```bash
$ cd ios/
$ pod install --repo-update
```

Once the Pods have installed locally, rebuild your iOS project:

```bash
npx react-native run-ios
```
```

### Android Installation

Source: https://rnfirebase.io/in-app-messaging/usage/installation/android

```mdx

# Android Manual Installation

The following steps are only required if your environment does not have access to React Native
auto-linking.

## 1. Update Gradle Settings

Add the following to your projects `/android/settings.gradle` file:

```groovy
include ':@react-native-firebase_inAppMessaging'
project(':@react-native-firebase_inAppMessaging').projectDir = new File(rootProject.projectDir, './../node_modules/@react-native-firebase/in-app-messaging/android')
```

## 2. Update Gradle Dependencies

Add the React Native Functions module dependency to your `/android/app/build.gradle` file:

```groovy
dependencies {
  // ...
  implementation project(path: ":@react-native-firebase_inAppMessaging")
}
```

## 3. Add package to the Android Application

Import and apply the React Native Firebase module package to your `/android/app/src/main/java/**/MainApplication.java` file:

Import the package:

```java
import io.invertase.firebase.fiam.ReactNativeFirebaseFiamPackage;
```

Add the package to the registry:

```java
protected List<ReactPackage> getPackages() {
  return Arrays.asList(
    new MainReactPackage(),
    new ReactNativeFirebaseFiamPackage(),
```

## 4. Rebuild the project

Once the above steps have been completed, rebuild your Android project:

```bash
npx react-native run-android
```
```

### iOS Setup

Source: https://rnfirebase.io/in-app-messaging/usage/installation/ios

```mdx

# iOS Manual Installation

The following steps are only required if your environment does not have access to React Native
auto-linking.

## 1. Add the Pod

Add the `RNFBInAppMessaging` Pod to your projects `/ios/Podfile`:

```ruby
target 'app' do
  ...
  pod 'RNFBInAppMessaging', :path => '../node_modules/@react-native-firebase/in-app-messaging'
end
```

## 2. Update Pods & rebuild the project

You may need to update your local Pods in order for the `RNFBInAppMessaging` Pod to be installed in your project:

```bash
$ cd ios/
$ pod install --repo-update
```

Once the Pods have installed locally, rebuild your iOS project:

```bash
npx react-native run-ios
```
```

### Android Setup

Source: https://rnfirebase.io/messaging/usage/installation/android

```mdx

# Android Manual Linking

The following steps are only required if your environment does not have access to React Native auto-linking.

## 1. Update Gradle Settings

Add the following to your project's `/android/settings.gradle` file:

```groovy
include ':@react-native-firebase_messaging'
project(':@react-native-firebase_messaging').projectDir = new File(rootProject.projectDir, './../node_modules/@react-native-firebase/messaging/android')
```

## 2. Update Gradle Dependencies

Add the React Native Functions module dependency to your `/android/app/build.gradle` file:

```groovy
dependencies {
  // ...
  implementation project(path: ":@react-native-firebase_messaging")
}
```

## 3. Add package to the Android Application

Import and apply the React Native Firebase module package to your `/android/app/src/main/java/**/MainApplication.java` file:

### 3.1 Import the package

Add the following underneath
`import com.facebook.react.ReactActivity;`:

```java
import io.invertase.firebase.messaging.ReactNativeFirebaseMessagingPackage;
```

### 3.2 Add the package to the registry

Add the following within the `MainActivity` class:

```java
protected List<ReactPackage> getPackages() {
  return Arrays.asList(
    new MainReactPackage(),
    new ReactNativeFirebaseMessagingPackage(),
  );
}
```

> If the method `getPackages()` already exists on the class in your project, then instead only add `new ReactNativeFirebaseMessagingPackage(),` to the returned list.

## 4. Rebuild the project

Once the above steps have been completed, rebuild your Android project:

```bash
npx react-native run-android
```
```

### iOS Installation

Source: https://rnfirebase.io/messaging/usage/installation/ios

```mdx

# iOS Manual Installation

The following steps are only required if your environment does not have access to React Native auto-linking.

## 1. Add the Pod

Add the `RNFBMessaging` Pod to your projects `/ios/Podfile`:

```ruby
target 'app' do
  # ...
  pod 'RNFBMessaging', :path => '../node_modules/@react-native-firebase/messaging'
end
```

## 2. Update Pods & rebuild the project

You may need to update your local Pods in order for the `RNFBMessaging` Pod to be installed in your project:

```bash
$ cd ios/
$ pod install --repo-update
```

Once the Pods have installed locally, rebuild your iOS project:

```bash
npx react-native run-ios
```
```

### Android Installation

Source: https://rnfirebase.io/ml/usage/installation/android

```mdx

# Android Manual Installation

The following steps are only required if your environment does not have access to React Native
auto-linking.

## 1. Update Gradle Settings

Add the following to your projects `/android/settings.gradle` file:

```groovy
include ':@react-native-firebase_ml'
project(':@react-native-firebase_ml').projectDir = new File(rootProject.projectDir, './../node_modules/@react-native-firebase/ml/android')
```

## 2. Update Gradle Dependencies

Add the React Native Functions module dependency to your `/android/app/build.gradle` file:

```groovy
// ..
dependencies {
  // ..
  implementation project(path: ":@react-native-firebase_ml")
}
```

## 3. Add package to the Android Application

Import and apply the React Native Firebase module package to your `/android/app/src/main/java/**/MainApplication.java` file:

Import the package:

```java
import io.invertase.firebase.perf.ReactNativeFirebaseMLPackage;
```

Add the package to the registry:

```java
protected List<ReactPackage> getPackages() {
  return Arrays.asList(
    new MainReactPackage(),
    new ReactNativeFirebaseMLPackage(),
```

## 4. Rebuild the project

Once the above steps have been completed, rebuild your Android project:

```bash
npx react-native run-android
```
```

### iOS Installation

Source: https://rnfirebase.io/ml/usage/installation/ios

```mdx

# iOS Manual Installation

The following steps are only required if your environment does not have access to React Native
auto-linking.

## 1. Add the Pod

Add the `RNFBML` Pod to your projects `/ios/Podfile`:

```ruby
target 'app' do
  # ...
  pod 'RNFBML', :path => '../node_modules/@react-native-firebase/ml'
end
```

## 2. Update Pods & rebuild the project

You may need to update your local Pods in order for the `RNFBML` Pod to be installed in your project:

```bash
$ cd /ios/
$ pod install --repo-update
```

Once the Pods have installed locally, rebuild your iOS project:

```bash
npx react-native run-ios
```
```

### Android Setup

Source: https://rnfirebase.io/perf/usage/installation/android

```mdx

# Android Manual Installation

The following steps are only required if your environment does not have access to React Native
auto-linking.

## 1. Update Gradle Settings

Add the following to your projects `/android/settings.gradle` file:

```groovy
include ':@react-native-firebase_perf'
project(':@react-native-firebase_perf').projectDir = new File(rootProject.projectDir, './../node_modules/@react-native-firebase/perf/android')
```

## 2. Update Gradle Dependencies

Add the React Native Functions module dependency to your `/android/app/build.gradle` file:

```groovy
dependencies {
  // ...
  implementation project(path: ":@react-native-firebase_perf")
}
```

## 3. Add package to the Android Application

Import and apply the React Native Firebase module package to your `/android/app/src/main/java/**/MainApplication.java` file:

Import the package:

```java
import io.invertase.firebase.perf.ReactNativeFirebasePerfPackage;
```

Add the package to the registry:

```java
protected List<ReactPackage> getPackages() {
  return Arrays.asList(
    new MainReactPackage(),
    new ReactNativeFirebasePerfPackage(),
```

## 4. Rebuild the project

Once the above steps have been completed, rebuild your Android project:

```bash
npx react-native run-android
```
```

### iOS Installation

Source: https://rnfirebase.io/perf/usage/installation/ios

```mdx

# iOS Manual Installation

The following steps are only required if your environment does not have access to React Native
auto-linking.

## 1. Add the Pod

Add the `RNFBPerf` Pod to your projects `/ios/Podfile`:

```ruby
target 'app' do
  # ...
  pod 'RNFBPerf', :path => '../node_modules/@react-native-firebase/perf'
end
```

## 2. Update Pods & rebuild the project

You may need to update your local Pods in order for the `RNFBPerf` Pod to be installed in your project:

```bash
$ cd ios/
$ pod install --repo-update
```

Once the Pods have installed locally, rebuild your iOS project:

```bash
npx react-native run-ios
```
```

### Android Installation

Source: https://rnfirebase.io/remote-config/usage/installation/android

```mdx

# Android Manual Installation

The following steps are only required if your environment does not have access to React Native
auto-linking.

## 1. Add Remote Config to Gradle Settings

Add the following to your projects `/android/settings.gradle` file:

```groovy
include ':@react-native-firebase_config'
project(':@react-native-firebase_config').projectDir = new File(rootProject.projectDir, './../node_modules/@react-native-firebase/remote-config/android')
```

## 2. Update Gradle Dependencies

Add the React Native Functions module dependency to your `/android/app/build.gradle` file:

```groovy
dependencies {
  implementation project(path: ":@react-native-firebase_config")
}
```

## 3. Add package to the Android Application

Import and apply the React Native Firebase module package to your `/android/app/src/main/java/**/MainApplication.java` file:

Import the package:

```java
import io.invertase.firebase.config.ReactNativeFirebaseConfigPackage;
```

Add the package to the registry:

```java
protected List<ReactPackage> getPackages() {
  return Arrays.asList(
    new MainReactPackage(),
    new ReactNativeFirebaseConfigPackage(),
```

## 4. Rebuild the project

Once the above steps have been completed, rebuild your Android project:

```bash
npx react-native run-android
```
```

### iOS Installation

Source: https://rnfirebase.io/remote-config/usage/installation/ios

```mdx

# iOS Manual Installation

The following steps are only required if your environment does not have access to React Native
auto-linking.

## 1. Add the Pod

Add the `RNFBRemoteConfig` Pod to your projects `/ios/Podfile`:

```ruby
target 'app' do
  # ...
  pod 'RNFBRemoteConfig', :path => '../node_modules/@react-native-firebase/remote-config'
end
```

## 2. Update Pods & rebuild the project

You may need to update your local Pods in order for the `RNFBRemoteConfig` Pod to be installed in your project:

```bash
$ cd ios/
$ pod install --repo-update
```

Once the Pods have installed locally, rebuild your iOS project:

```bash
npx react-native run-ios
```
```

### Android Installation

Source: https://rnfirebase.io/storage/usage/installation/android

```mdx

# Android Manual Installation

The following steps are only required if your environment does not have access to React Native
auto-linking.

## 1. Update Gradle Settings

Add the following to your projects `/android/settings.gradle` file:

```groovy
include ':@react-native-firebase_storage'
project(':@react-native-firebase_storage').projectDir = new File(rootProject.projectDir, './../node_modules/@react-native-firebase/storage/android')
```

## 2. Update Gradle Dependencies

Add the React Native Functions module dependency to your `/android/app/build.gradle` file:

```groovy
dependencies {
  // ...
  implementation project(path: ":@react-native-firebase_storage")
}
```

## 3. Add package to the Android Application

Import and apply the React Native Firebase module package to your `/android/app/src/main/java/**/MainApplication.java` file:

Import the package:

```java
import io.invertase.firebase.storage.ReactNativeFirebaseStoragePackage;
```

Add the package to the registry:

```java
protected List<ReactPackage> getPackages() {
  return Arrays.asList(
    new MainReactPackage(),
    new ReactNativeFirebaseStoragePackage(),
```

## 4. Rebuild the project

Once the above steps have been completed, rebuild your Android project:

```bash
npx react-native run-android
```
```

### iOS Installation

Source: https://rnfirebase.io/storage/usage/installation/ios

```mdx

# iOS Manual Installation

The following steps are only required if your environment does not have access to React Native
auto-linking.

## 1. Add the Pod

Add the `RNFBStorage` Pod to your projects `/ios/Podfile`:

```ruby
target 'app' do
  # ...
  pod 'RNFBStorage', :path => '../node_modules/@react-native-firebase/storage'
end
```

## 2. Update Pods & rebuild the project

You may need to update your local Pods in order for the `RNFBStorage` Pod to be installed in your project:

```bash
$ cd ios/
$ pod install --repo-update
```

Once the Pods have installed locally, rebuild your iOS project:

```bash
npx react-native run-ios
```
```
