How to Prevent Users From Taking Screenshots in React-Native Apps

Photo of Dawid Przydróżny

Dawid Przydróżny

Updated Jan 3, 2023 • 12 min read
smartphone

There can be many reasons to prevent screenshot-taking in a React Native application, but keep in mind that it is bad protection for secure data or anything that user should not save, see or remember.

A simple example of a work-around is to take a photo of the device with another one.

Anyway, is it even possible?

Android

It's quite simple to efficiently prevent screenshot-taking in Android.

There are two approaches you might take. The first one is to forbid screenshot taking in the whole application (easier to implement) and the second one is to handle the forbid and allow methods from JavaScript code (this approach requires you to implement a bridge from Android to JavaScript code).

Forbid screenshot taking in whole application

Add the code below with + signs to the MainActivity.java file located here: android/app/src/main/java/com/your-app name/MainActivity.java


Note: This is a full file example (RN 0.61.5 version). If your project already contains this file, you can copy its entire content and replace your-app-name with your application's real name. You can find this at the top in the MainApplication.java or MainActivity.java file. Don't forget to remove the + signs!

package com.your-app-name;

import com.facebook.react.ReactActivity;
+ import android.view.WindowManager;
+ import android.os.Bundle;

public class MainActivity extends ReactActivity {

  /**
   * Returns the name of the main component registered from JavaScript. This is used to schedule
   * rendering of the component.
   */
  @Override
  protected String getMainComponentName() {
    return "your-app-name";
  }
  
  + @Override
  + protected void onCreate(Bundle savedInstanceState) {
  +   super.onCreate(savedInstanceState);
  +
  +   getWindow().setFlags(
  +     WindowManager.LayoutParams.FLAG_SECURE,
  +     WindowManager.LayoutParams.FLAG_SECURE
  +   );
  + }
}

That's it. Screenshot-taking is locked now in whole application.

Forbid and allow screenshot-taking using JavaScript code

Firstly, create a new file under this path: android/app/src/main/java/com/your-app-name/PreventScreenshotModule.java

I named my module PreventScreenshotModule.java but, of course, module's name is up to you

Note: Change your-app-name to your application's real name. Also make sure to rename the "PreventScreenshotModule" fields in this code in case you chose a different name. Feel free to change the Promises output messages as well

Add the following code

package com.your-app-name;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;

import android.view.WindowManager;

import static com.facebook.react.bridge.UiThreadUtil.runOnUiThread;

public class PreventScreenshotModule extends ReactContextBaseJavaModule {
  private static final String PREVENT_SCREENSHOT_ERROR_CODE = "PREVENT_SCREENSHOT_ERROR_CODE";
  private final ReactApplicationContext reactContext;

  PreventScreenshotModule(ReactApplicationContext context) {
    super(context);
    reactContext = context;
  }

  @Override
  public String getName() {
    return "PreventScreenshotModule";
  }

  @ReactMethod
  public void forbid(Promise promise) {
    runOnUiThread(new Runnable() {
      @Override
      public void run() {
        try {
          getCurrentActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
          promise.resolve("Done. Screenshot taking locked.");
        } catch(Exception e) {
          promise.reject(PREVENT_SCREENSHOT_ERROR_CODE, "Forbid screenshot taking failure.");
        }
      }
    });
  }

  @ReactMethod
  public void allow(Promise promise) {
    runOnUiThread(new Runnable() {
      @Override
      public void run() {
        try {
          getCurrentActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
          promise.resolve("Done. Screenshot taking unlocked.");
        } catch (Exception e) {
          promise.reject(PREVENT_SCREENSHOT_ERROR_CODE, "Allow screenshot taking failure.");
        }
      }
    });
  }
}

Create another file (again, the name is up to you) under the same path: android/app/src/main/java/com/your-app-name/CustomPreventScreenshotPackage.java

Note: Change your-app-name to your application's real name. Also make sure to edit all the "CustomPreventScreenshotPackage" fields in this code in case you chose a different package name

Note: Paste your module's name if you changed it from "PreventScreenshotModule"

package com.your-app-name;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class CustomPreventScreenshotPackage implements ReactPackage {

  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Collections.emptyList();
  }

  @Override
  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
   return Arrays.<NativeModule>asList(new PreventScreenshotModule(reactContext));
  }
}

Open the MainApplication.java file located under this path android/app/src/main/java/com/your-app-name/MainApplication.java

Add import on top of the file and add the package to the getPackages() method in the MainApplication class, follow the + signs in the code below.

Note: Change the package's name if you picked a different name (in both the import section and the actual code).

+ import com.prevent_screenshot.CustomPreventScreenshotPackage;

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost =
      new ReactNativeHost(this) {

      ...

      @Override
      protected List<ReactPackage> getPackages() {
        @SuppressWarnings("UnnecessaryLocalVariable")
        List<ReactPackage> packages = new PackageList(this).getPackages();
        // Packages that cannot be autolinked yet can be added manually here, for example:
        // packages.add(new MyReactNativePackage());
      + packages.add(new CustomPreventScreenshotPackage());
        return packages;
      }

      ...

Now you can access the allow and forbid methods inside of the JavaScript code; the module's name depends on your name, in this case it's PreventScreenshotModule from NativeModules

Check out this example

import { NativeModules } from 'react-native';

const forbidFunction = async () => {
  try {
    const result = await NativeModules.PreventScreenshotModule.forbid();
    console.log(result);
  } catch (e) {
    console.log(e);
  }
}

const allowFunction = async () => {
  try {
    const result = await NativeModules.PreventScreenshotModule.allow();
    console.log(result);
  } catch (e) {
    console.log(e);
  }
}

iOS

Unfortunately, it's almost impossible to prevent screenshot-taking on iOS devices. There is no simple or efficient solution. You can only get information AFTER taking a screenshot and display some information or pop-up to the user.

Historical note: Famous application Snapchat had this feature in the past. They used some kind of trick that relied on forcing users to constantly be touching the screen to open messages. This meant that if a user tried to take a screenshot, the system action that handles this would force them to stop touching the screen for a moment. As a result, the message would close and the resulting screenshot would show a different screen. Still, this solution is far from perfect and may not always work. Moreover, iOS made implementing this workaround impossible a few releases ago.

Current tricks and solutions: There are basically two ways to prevent users from taking screenshot. Neither is good in terms of performance and battery drain.

The first one is to create an invisible 60 fps animation on top of the "secured" screen. Because the iOS screenshot feature only saves a single frame, it would only capture the overlaid animation, but no the content you wish to hide.

The second one is paying for ScreenShieldKit, which is probably a variation of the solution described above.

Photo of Dawid Przydróżny

More posts by this author

Dawid Przydróżny

Efficient software development  Build faster, deliver more  Start now!

Read more on our Blog

Check out the knowledge base collected and distilled by experienced professionals.

We're Netguru

At Netguru we specialize in designing, building, shipping and scaling beautiful, usable products with blazing-fast efficiency.

Let's talk business