How to display a PDF in React Native

As I move up the learning curve on React Native, I thought it would helpful to share a few tips and tricks I’ve learned.  One of the more challenging things I’ve come across was how to show a PDF file in your app. I couldn’t find any tutorial to demonstrate how to do this across both iOS and Android, so I thought I would write my own:

iOS

This one is fairly easy. The WebView in iOS has native support for PDF rendering. It’s just a matter of tell it where to find your PDF:

render: function() {
  return <WebView source={{uri: 'Your.pdf'}}/>
}

The one trick part is that you need to include Your.pdf into your project in Xcode and make sure it’s added to your build target.

Just copying it into your React Native project folder isn’t enough. It has to be part of the Xcode project itself.

Android

This one took a bit more work to figure out.

It appears that Android did not provide a native PDF viewer until version 5.0 (Lollipop). To provide a solution with broader support for older Android devices, you will need to make use of three key techniques:

  1. Pull the PDF out of my APK bundle and store it in the files folder for your app. This Stack Overflow answer was very helpful in accomplishing this:

Android: How to copy files from ‘assets’ folder to sdcard?

I tweaked the code a bit so that the file wasn’t going to an sdcard but to your app’s files folder. Here’s what to add to your MainActivity.java file:

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  AssetManager assetManager = getAssets();
  String[] files = null;

  try {
      files = assetManager.list("pdf");
  } catch (IOException e) {
      Log.e("tag", "Failed to get asset file list.", e);
  }

  if (files != null) for (String filename : files) {
      InputStream in = null;
      OutputStream out = null;

      try {
        in = assetManager.open("pdf/" + filename);

        File outFile = new File(getFilesDir(), filename);
        out = new FileOutputStream(outFile);
        copyFile(in, out);
        Log.e("tag", "Copy was a success: " + outFile.getPath());
      } catch(IOException e) {
        Log.e("tag", "Failed to copy asset file: " + "pdf/" + filename, e);
      }
      finally {
          if (in != null) {
              try {
                  in.close();
              } catch (IOException e) {
                  // NOOP
              }
          }
          if (out != null) {
              try {
                  out.close();
              } catch (IOException e) {
                  // NOOP
              }
          }
      }
  }
}

private void copyFile(InputStream in, OutputStream out) throws IOException {
    byte[] buffer = new byte[1024];
    int read;
    while((read = in.read(buffer)) != -1){
      out.write(buffer, 0, read);
    }
}

Make sure to place your PDF file in the assets/pdf folder under android/app/src/main

  1. Next you’ll be using the react-native-fs package to get the absolute URL to your PDF, which is now in the files folder:
    var RNFS = require('react-native-fs');
    var absolutePath = RNFS.DocumentDirectoryPath + '/Your.pdf';
    
  2. With all of this in place, use react-native-pdf-view to actually load and display the PDF:
    import PDFView from 'react-native-pdf-view';
    
    render: function() {
      var absolutePath = RNFS.DocumentDirectoryPath + '/Your.pdf';
    
      return <PDFView
        ref={(pdf)=>{this.pdfView = pdf;}}
        src={absolutePath}
        style={ActharStyles.fullCover} />
    }
    

Not exactly dirt simple, but it works well enough.

So there you go!  You can now display a PDF file in your React Native project on either iOS or Android. FYI, I also wrote this up as an answer to my own Stack Overflow question. If you have suggestions for how to improve the technique, you may want to post them there.

Posted in Mobile, Tips & Tricks