All guides
React Native — GuideReact Native

Detecting Fake GPS in React Native

Use react-native-mock-location-detector to catch mock-location apps on Android, gate your UI, and know when to move to the maintained fork.

Syed Wajeeh Haider
Syed Wajeeh Haider
Full Stack Software Engineer
Sep 28, 20265 min read
#React Native#Android#Location#Security

If your app pays drivers per trip, checks employees in by location, or rewards players for visiting places, someone will eventually try a fake GPS app. On Android, mock-location apps are a few taps away in Developer options. The good news is that Android marks fixes coming from a mock provider, and a small React Native package exposes that flag to JavaScript.

1 method
checkMockLocationProvider(), Android only
API 31
Android 12: isMock() replaces the deprecated isFromMockProvider()
iOS 15
CLLocationSourceInformation.isSimulatedBySoftware added

How the detector works

The package react-native-mock-location-detector (latest published version 2.0.0) is a thin native module. Its default export, RNMockLocationDetector, has exactly one method: checkMockLocationProvider(), which returns a Promise<boolean>. The README mentions passing a dialog title, text and button label, but the actual code takes no arguments and shows no dialog. Any UI is yours to build.

On the native side it asks Google Play services' fused location provider for the last known location via FusedLocationProviderClient.getLastLocation(), then calls Location.isFromMockProvider() on it (available since API 18). On very old devices it falls back to the Settings.Secure.ALLOW_MOCK_LOCATION setting.

Modern React Native autolinks native modules, so there is no react-native link step. You do need a native rebuild, and the device needs Google Play services because the check uses the fused provider. The package's own manifest already declares ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION.

bash
npm install react-native-mock-location-detector

# rebuild the Android app so the native module is compiled in
npx react-native run-android

A useMockLocationGuard hook

Wrap the permission request and the check in a hook that exposes a clear state. Because the package is Android only, guard with Platform.OS so iOS builds never call a module that does not exist. Re-check whenever the app returns to the foreground with AppState: users often switch to their spoofing app and back.

ts
import { useCallback, useEffect, useState } from 'react';
import { AppState, PermissionsAndroid, Platform } from 'react-native';
import RNMockLocationDetector from 'react-native-mock-location-detector';

export type GuardState =
  | 'checking'
  | 'ok'
  | 'mocked'
  | 'no-permission'
  | 'unsupported';

export function useMockLocationGuard() {
  const [state, setState] = useState<GuardState>('checking');

  const check = useCallback(async () => {
    if (Platform.OS !== 'android') {
      setState('unsupported');
      return;
    }

    const result = await PermissionsAndroid.request(
      PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
    );
    if (result !== PermissionsAndroid.RESULTS.GRANTED) {
      setState('no-permission');
      return;
    }

    const mocked = await RNMockLocationDetector.checkMockLocationProvider();
    setState(mocked ? 'mocked' : 'ok');
  }, []);

  useEffect(() => {
    check();
    const sub = AppState.addEventListener('change', (next) => {
      if (next === 'active') check();
    });
    return () => sub.remove();
  }, [check]);

  return { state, recheck: check };
}
useMockLocationGuard.ts

PermissionsAndroid.request returns immediately with GRANTED if the user already allowed location, so calling it on every check is cheap. If the user picked "Don't allow" permanently, it returns NEVER_ASK_AGAIN, and your no-permission screen should explain how to enable it in system settings.

Gate the experience, kindly

When a mock provider is detected, block only the location-sensitive flows and explain what to do. Most users who trip the check are developers or testers who forgot a mock app was enabled, not fraudsters. The exact path to Developer options differs between manufacturers, so describe it in general terms rather than promising a specific screen.

tsx
import type { ReactNode } from 'react';
import { ActivityIndicator, Button, Text, View } from 'react-native';
import { useMockLocationGuard } from './useMockLocationGuard';

export function MockLocationGate({ children }: { children: ReactNode }) {
  const { state, recheck } = useMockLocationGuard();

  if (state === 'checking') {
    return <ActivityIndicator style={{ flex: 1 }} />;
  }

  if (state === 'mocked') {
    return (
      <View style={{ flex: 1, justifyContent: 'center', padding: 24, gap: 12 }}>
        <Text style={{ fontSize: 22, fontWeight: '600' }}>Turn off mock location</Text>
        <Text>
          A mock location app is active on this device. Disable it in
          Developer options (or uninstall it), then come back.
        </Text>
        <Button title="I've turned it off" onPress={recheck} />
      </View>
    );
  }

  if (state === 'no-permission') {
    return (
      <View style={{ flex: 1, justifyContent: 'center', padding: 24, gap: 12 }}>
        <Text>Location access is needed to continue.</Text>
        <Button title="Try again" onPress={recheck} />
      </View>
    );
  }

  return <>{children}</>;
}
MockLocationGate.tsx

Moving to the maintained fork for iOS

The original package's README states that the author no longer maintains it, and recommends react-native-turbo-mock-location-detector by jpudysz. The fork supports both the old and new React Native architectures and adds iOS. On Android it uses location.isMock on API 31 and above, and isFromMockProvider below that. On iOS 15 and above it reads CLLocationSourceInformation.isSimulatedBySoftware.

ts
import {
  isMockingLocation,
  MockLocationDetectorErrorCode,
  type MockLocationDetectorError,
} from 'react-native-turbo-mock-location-detector';

export type MockCheck = 'ok' | 'mocked' | 'gps-off' | 'no-permission' | 'unknown';

export async function checkMockLocation(): Promise<MockCheck> {
  try {
    const { isLocationMocked } = await isMockingLocation();
    return isLocationMocked ? 'mocked' : 'ok';
  } catch (e) {
    const error = e as MockLocationDetectorError;
    switch (error.code) {
      case MockLocationDetectorErrorCode.GPSNotEnabled:
        return 'gps-off';
      case MockLocationDetectorErrorCode.NoLocationPermissionEnabled:
        return 'no-permission';
      case MockLocationDetectorErrorCode.CantDetermine:
      default:
        return 'unknown'; // always the case on iOS below 15
    }
  }
}
Import from react-native-turbo-mock-location-detector (its README example shows the old package name)

Unlike the original, the fork rejects with a { code, message } error instead of quietly returning false, which makes the "could not check" case explicit. Run pod install in the ios folder after installing, and note that it will not work in Expo Go: Expo projects need a development build that includes the native module.

Key takeaways

  • —react-native-mock-location-detector exposes one Android-only method, checkMockLocationProvider(), which takes no arguments.
  • —It inspects the last known fused location, so request permission and get a fix first; false only means nothing was detected.
  • —Wrap it in a hook, guard with Platform.OS, and re-check when the app returns to the foreground.
  • —Block only location-sensitive flows and explain how to turn the mock app off.
  • —For iOS 15+, the new architecture and explicit error codes, use react-native-turbo-mock-location-detector.

Detection on the device is only half the story. Next, read Why Client-Side Fake GPS Checks Aren't Enough to see how to validate location on the server with speed, time and integrity signals.

Keep reading
(10)Let's talk

Ready to scaleyour application?

Stop worrying about technical debt. I can help you build a reliable, high-performance product from the ground up, tailored exactly to your business needs.

Actively open for new opportunities
Get in touch
Local time
--:-- PKT
© 2026 Syed Wajeeh Haider. All rights reserved.