npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

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

About

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

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

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

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

Open Software & Tools

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

© 2026 – Pkg Stats / Ryan Hefner

react-native-app-device-info

v1.0.0

Published

Minimal, fast, synchronous app & device info for React Native — app version, build number, OS version, device name/type and a persistent unique device id. iOS + Android, Old & New Architecture.

Readme

react-native-app-device-info

Minimal, fast, synchronous app and device information for React Native.

Every value is read from native constants that are computed once when the app starts. From JavaScript there is no Promise, no await, and no bridge round-trip. Every getter is a plain synchronous function call. The library works on iOS and Android, on both the Old and the New Architecture.

import AppDeviceInfo from 'react-native-app-device-info';

AppDeviceInfo.getVersion();     // "1.2.3"
AppDeviceInfo.getBuildNumber(); // "42"
AppDeviceInfo.getUniqueId();    // a stable id for this device

Table of contents


Why this library

  • Small. One native module, about twenty getters, zero runtime dependencies.
  • Fast. Values are exposed as native constants, so reading them is just a property access. There is no message sent over the bridge and no startup penalty.
  • Simple. Synchronous getters. No callbacks, no promises, nothing to wait for.
  • Consistent. The same API and the same return types on iOS and Android.
  • Future proof. Identical behaviour on the legacy bridge and the New Architecture (bridgeless / TurboModules interop).

Requirements

  • React Native 0.64 or newer (Old or New Architecture).
  • iOS 12.0 or newer.
  • Android API level 21 (Android 5.0) or newer.

Installation

npm install react-native-app-device-info

or

yarn add react-native-app-device-info

This package contains native code, so you must rebuild the app after installing it. A Fast Refresh / Metro reload is not enough.

iOS

Install the CocoaPods dependency, then rebuild:

cd ios && pod install && cd ..
npx react-native run-ios

Android

Autolinking handles everything. Just rebuild the app:

npx react-native run-android

Expo

This is a native module, so it does not run in Expo Go. Use a development build:

npx expo install react-native-app-device-info
npx expo prebuild
npx expo run:ios     # or: npx expo run:android

Quick start

You can import the default object and call methods on it:

import AppDeviceInfo from 'react-native-app-device-info';

console.log(AppDeviceInfo.getReadableVersion()); // "1.2.3 (42)"
console.log(AppDeviceInfo.getOsName(), AppDeviceInfo.getOsVersion()); // "iOS" "17.4"
console.log(AppDeviceInfo.getUniqueId());

Or import only the functions you need:

import { getVersion, getBuildNumber, getInfo } from 'react-native-app-device-info';

const version = getVersion();
const build = getBuildNumber();
const everything = getInfo();

Both styles are equivalent and fully typed.


API reference

Every function is synchronous and returns immediately. The tables show the return type and the native source on each platform.

App information

| Function | Returns | iOS | Android | | ---------------------- | -------- | ---------------------------- | ---------------------------------------------------- | | getVersion() | string | CFBundleShortVersionString | versionName | | getBuildNumber() | string | CFBundleVersion | versionCode | | getBundleId() | string | bundleIdentifier | packageName (runtime applicationId, incl. suffix) | | getBaseBundleId() | string | bundle id without suffix | applicationId without suffix | | getAppName() | string | display name | application label | | getReadableVersion() | string | "1.2.3 (42)" | "1.2.3 (42)" |

getVersion() is the human-facing version (the "version name" / "build name"). getBuildNumber() is the internal incrementing number (the "build number" / "version code").

Device and OS information

| Function | Returns | iOS | Android | | ------------------- | -------------------- | --------------------- | ------------------------ | | getOsName() | string | "iOS" | "Android" | | getOsVersion() | string | systemVersion | Build.VERSION.RELEASE | | getDeviceType() | 'ios' \| 'android' | "ios" | "android" | | getDeviceModel() | string | e.g. "iPhone15,2" | Build.MODEL | | getDeviceBrand() | string | "Apple" | Build.MANUFACTURER | | isTablet() | boolean | iPad? | large screen? | | getUniqueId() | string | identifierForVendor | Settings.Secure.ANDROID_ID | | isEmulator() | boolean | running on simulator? | running on emulator? | | getDeviceLocale() | string | e.g. "en-US" | e.g. "en-US" | | getTimezone() | string | e.g. "America/New_York" | e.g. "America/New_York" | | getApiLevel() | number | 0 (not applicable) | Build.VERSION.SDK_INT |

Install information

| Function | Returns | iOS | Android | | ----------------------- | -------- | ------------------------------------------ | ----------------------------------------------------------------- | | getInstallerSource() | string | "AppStore" / "TestFlight" / "Other" | installing package, e.g. "com.android.vending", or "" | | getFirstInstallTime() | number | epoch milliseconds (heuristic) | epoch milliseconds (exact) | | getLastUpdateTime() | number | epoch milliseconds (heuristic) | epoch milliseconds (exact) |

Times are epoch milliseconds. Wrap a value with new Date(ms) to get a Date. A value of 0 means the time could not be determined.

Get everything at once

getInfo() returns every value in a single object. The result is computed once and cached, so calling it repeatedly is free.

import { getInfo } from 'react-native-app-device-info';

const info = getInfo();

The returned object has this exact shape:

interface AppDeviceInfoConstants {
  // App
  appVersion: string;       // "1.2.3"
  buildNumber: string;      // "42"
  bundleId: string;         // "com.acme.app"
  appName: string;          // "Acme"

  // Device and OS
  osName: string;           // "iOS" | "Android"
  osVersion: string;        // "17.4"
  deviceType: 'ios' | 'android';
  deviceModel: string;      // "iPhone15,2" | "Pixel 8"
  deviceBrand: string;      // "Apple" | "Google"
  isTablet: boolean;
  deviceId: string;         // stable per-device id
  isEmulator: boolean;
  deviceLocale: string;     // "en-US"
  timezone: string;         // "America/New_York"
  apiLevel: number;         // 34 on Android, 0 on iOS

  // Install
  installerSource: string;  // "AppStore" | "com.android.vending" | ...
  firstInstallTime: number; // epoch ms, 0 if unknown
  lastUpdateTime: number;   // epoch ms, 0 if unknown
}

Note that getInfo().appVersion is the value behind getVersion(), getInfo().buildNumber is the value behind getBuildNumber(), and so on. The individual getters are thin convenience wrappers around this object.


Field notes

This section explains the values that need context so you choose the right one and know exactly what it guarantees.

getUniqueId

A stable id for the device that persists across app launches and app updates.

  • iOS returns identifierForVendor. It stays the same while at least one app from the same vendor (the same Apple Developer account) is installed. If the user deletes every app from that vendor and reinstalls, a new id is generated.
  • Android returns Settings.Secure.ANDROID_ID. It stays the same until a factory reset. On Android 8.0 and newer the value is scoped to your app's signing key, so different apps see different ids on the same device.

This is the identifier that both platforms recommend for ordinary app use. It needs no extra permissions and does not expose a hardware serial number. It is not designed for cross-vendor tracking and is not guaranteed to be globally unique across the entire world, only stable for your app on a given device.

getBundleId vs getBaseBundleId

  • getBundleId() returns the identifier of the build that is actually running. On Android this includes any applicationIdSuffix from your Gradle config, so a debug build can report com.acme.app.debug. On iOS it is whatever bundle identifier the running build was signed with, including any .dev or .staging suffix.
  • getBaseBundleId() returns the same value with one recognised build-variant suffix removed. For example com.acme.app.debug becomes com.acme.app.

The suffixes that are stripped are: debug, dev, development, staging, stg, qa, test, alpha, beta, release, internal. Only the final segment is checked, and only if it matches that list, so a normal id such as com.acme.app is never shortened by mistake. Use getBaseBundleId() when you want one identity for an app across all of its build variants, for example as a key in analytics or backend records.

getInstallerSource

Tells you where the installed build came from.

  • Android returns the package name of the installer. Common values are com.android.vending (Google Play) and com.amazon.venezia (Amazon Appstore). A build installed with adb, a file manager, or a CI device farm usually returns an empty string.
  • iOS has no public installer API, so the library infers the source from the App Store receipt: "TestFlight" for a TestFlight build, "AppStore" for a build delivered through the App Store, and "Other" for development, simulator, or sideloaded builds.

Install and update times

getFirstInstallTime() and getLastUpdateTime() return epoch milliseconds.

  • Android reads exact values from PackageInfo.firstInstallTime and PackageInfo.lastUpdateTime.
  • iOS has no public API for these, so the library uses a filesystem heuristic: the creation date of the app's Documents directory approximates the first install, and the modification date of the app executable approximates the last update. Treat the iOS numbers as a best effort rather than an exact record.

isEmulator

Returns true when the app is running on a simulator (iOS) or an emulator (Android).

  • iOS uses a compile-time simulator flag, so the result is reliable.
  • Android has no single official flag, so the library inspects standard Build properties (fingerprint, model, hardware, manufacturer, product). This detects the common emulators, including the Android Studio AVDs and Genymotion. Treat it as a strong best effort rather than a guarantee.

How it works

The native module implements the classic React Native constants API (constantsToExport on iOS, getConstants() on Android). The platform builds a single dictionary of values when the module is created and hands it to JavaScript. The JavaScript layer reads that dictionary once, normalises every value, caches the result, and returns it from each getter.

Because the values are constants rather than method calls, there is no asynchronous message and no serialization cost at call time. This is why every function in the API can be synchronous, and why the library adds effectively nothing to startup beyond reading a handful of system properties.

The JavaScript layer also supports both shapes that React Native can hand it: constants attached directly to the module object (legacy bridge) and a getConstants() method (New Architecture interop). You do not need to configure anything for either case.


TypeScript

The package ships its own type definitions. The default export, every named function, and the AppDeviceInfoConstants interface are fully typed. No @types package is required.

import AppDeviceInfo, {
  getInfo,
  type AppDeviceInfoConstants,
} from 'react-native-app-device-info';

const info: AppDeviceInfoConstants = getInfo();

Platform support matrix

| Value | iOS | Android | | ------------------ | ------------------------- | ---------------------- | | App version | Yes | Yes | | Build number | Yes | Yes | | Bundle / package id| Yes | Yes | | Base bundle id | Yes | Yes | | App name | Yes | Yes | | OS name / version | Yes | Yes | | Device type | Yes | Yes | | Device model | Yes | Yes | | Device brand | "Apple" | Yes | | Is tablet | Yes | Yes | | Unique id | Yes (identifierForVendor) | Yes (ANDROID_ID) | | Is emulator | Yes (reliable) | Yes (heuristic) | | Locale | Yes | Yes | | Timezone | Yes | Yes | | API level | 0 (not applicable) | Yes | | Installer source | Yes (inferred) | Yes | | First install time | Heuristic | Yes (exact) | | Last update time | Heuristic | Yes (exact) |


Troubleshooting

The module is not linked / values throw an error. Rebuild the native app after installing. A Metro reload does not include new native code. On iOS, run pod install first.

It does not work in Expo Go. Expo Go cannot load custom native modules. Use a development build with npx expo prebuild and npx expo run:ios or npx expo run:android.

getBuildNumber() returns a different value than I set. It returns the value of the build that is actually running. Make sure you are looking at the same build configuration (debug vs release) that you edited.

getApiLevel() returns 0. That is expected on iOS, which has no Android-style API level. Use getOsVersion() on iOS instead.

getFirstInstallTime() looks wrong on iOS. iOS does not expose an install timestamp, so the value is a filesystem-based estimate. Use the Android values when you need exact timing.


License

MIT