Blog Post

How to Add Annotations to a PDF Using React Native

Illustration: How to Add Annotations to a PDF Using React Native

In this post, you’ll learn how to use React Native to add annotations to PDF documents.

PSPDFKit lets you easily add document annotation functionality to your React Native application. This tutorial will explore how to add ink and text annotations, how to set an annotation author, and how to embed annotations into PDFs.

PSPDFKit React Native PDF Library

PSPDFKit offers a commercial React Native PDF library for viewing, generating, annotating, and editing PDFs. You can use it to quickly add PDF functionality to your React Native applications.

It offers a variety of additional features and capabilities, including:

Requirements

For this tutorial, you’ll need a React Native development environment for running React Native projects using the React Native command-line interface (CLI) — not the Expo CLI — and configured for the platforms you want to build (Android, iOS, or both).

Dependencies

There are two dependencies you’ll need:

Getting Started

Here’s an overview of the steps you’ll follow:

  1. Installing the necessary dependencies

  2. Displaying a PDF document

  3. Creating ink annotations

  4. Adding text annotations

  5. Setting an annotation author

  6. Embedding annotations into a PDF

Installing the Necessary Dependencies

  1. Create a fresh React Native project:

    npx react-native init PSPDFKitAnnotations
  2. Change to the project directory and add the PSPDFKit plugin:

    cd PSPDFKitAnnotations
    yarn add github:PSPDFKit/react-native
Information

The PSPDFKit React Native dependency is installed from the GitHub repository and not the npm registry. To install the PSPDFKit React Native dependency, run yarn add github:PSPDFKit/react-native in your project directory or npm install github:PSPDFKit/react-native if you’re using npm.

  1. Add the react-native-fs dependency:

    yarn add react-native-fs
  2. Now, install all the dependencies for the project:

    yarn install
  3. Open your project’s android/build.gradle file and add the PSPDFKit repository to download the PSPDFKit library:

...
  allprojects {
      repositories {
          mavenLocal()
  +        maven {
  +            url 'https://my.pspdfkit.com/maven/'
  +        }
...

6. Open your project’s `ios/Podfile` in a text editor to update the platform to iOS 14, and add the PSPDFKit Podspec:

```diff
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'

- platform :ios, '11.0'
+ platform :ios, '14.0'

target 'PSPDFKitDemo' do
 config = use_native_modules!

 use_react_native!(
   :path => config[:reactNativePath],
   # To enable Hermes on iOS, change `false` to `true` and then install pods.
   :hermes_enabled => false
 )

 target 'PSPDFKitDemoTests' do
   inherit! :complete
   # Pods for testing
 end
+ pod 'PSPDFKit', podspec: 'https://my.pspdfkit.com/pspdfkit-ios/latest.podspec'
 # Enables Flipper.
 #
 # Note that if you have `use_frameworks!` enabled, Flipper won't work and
 # you should disable the next line.
 use_flipper!()

 post_install do |installer|
   react_native_post_install(installer)
 end
end
  1. For PSPDFKit for iOS SDK version 12.1.0 and above, it’s necessary to set the license key in the AppDelegate object before initializing PSPDFKit for the first time. Open the ios/PSPDFKitAnnotations/AppDelegate.mm file and add the following:

    // Add the import statement.
    #import <PSPDFKit/PSPDFKit.h>
    
    @implementation AppDelegate
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
    
    // Set the license key. Use an empty string when using a trial version.
    [PSPDFKitGlobal setLicenseKey:@""];
  2. Change to the ios directory and install the CocoaPods dependencies:

    cd ios
    pod install
  3. Open your project’s Workspace in Xcode:

    open PSPDFKitDemo.xcworkspace
  4. Make sure the deployment target is set to 14.0 or higher.

Image showing Xcode interface to set deployment target

  1. Change View controller-based status bar appearance to YES in your project’s Info.plist.

Image showing the project’s info.plist in Xcode

Displaying a PDF Document

  1. Add the PDF document you want to display to your application by dragging it into your project. In the dialogue that’s displayed, select Finish to accept the default integration options. You can use this Quickstart Guide PDF as an example.

  2. Change to the root of your project and create the assets directory:

    cd ..
    mkdir android/app/src/main/assets
  3. Then, copy the PDF document you want to display to the assets directory.

  4. Open the App.js file and delete all its contents. Then, add the following import statements:

    import React, { Component } from 'react';
    import { Platform } from 'react-native';
    import PSPDFKitView from 'react-native-pspdfkit';
  5. Point to the document you added earlier:

    const DOCUMENT =
    	Platform.OS === 'ios'
    		? 'Document.pdf'
    		: 'file:///android_asset/Document.pdf';
  6. Now, display the document with the PSPDFKitView component:

    <PSPDFKitView
    	document={DOCUMENT}
    	configuration={{
    		showThumbnailBar: 'scrollable',
    		pageTransition: 'scrollContinuous',
    		scrollDirection: 'vertical',
    	}}
    	ref="pdfView"
    	fragmentTag="PDF1"
    	style={{ flex: 1 }}
    />
  7. Put it all together:

    import React, { Component } from 'react';
    import { Platform } from 'react-native';
    import PSPDFKitView from 'react-native-pspdfkit';
    
    const DOCUMENT =
    	Platform.OS === 'ios'
    		? 'Document.pdf'
    		: 'file:///android_asset/Document.pdf';
    
    export default class PSPDFKitDemo extends Component {
    	constructor(props) {
    		super(props);
    		this.pdfRef = React.createRef();
    	}
    
    	render() {
    		return (
    			<PSPDFKitView
    				document={DOCUMENT}
    				configuration={{
    					showThumbnailBar: 'scrollable',
    					pageTransition: 'scrollContinuous',
    					scrollDirection: 'vertical',
    				}}
    				ref={this.pdfRef}
    				fragmentTag="PDF1"
    				style={{ flex: 1 }}
    			/>
    		);
    	}
    }
  8. Open your terminal and launch your application:

    npx react-native run-android

GIF showing the result of ‘npx react-native run-android’

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

GIF showing the result of ‘npx react-native run-ios’

Creating Ink Annotations

  1. PSPDFKit for React Native leverages Instant JSON to import and export annotations. The PDF changes, such as annotations, can be stored in a separate JSON file with Instant JSON and added as an overlay to the existing PDF. You’ll use the following JSON for this example:

const annotationJSONInk = {
    bbox: [
        89.586334228515625, 98.5791015625, 143.12948608398438,
        207.1583251953125,
    ],
    isDrawnNaturally: false,
    lineWidth: 5,
    lines: {
        intensities: [
            [0.5, 0.5, 0.5],
            [0.5, 0.5, 0.5],
        ],
        points: [
            [
                [92.086334228515625, 101.07916259765625],
                [92.086334228515625, 202.15826416015625],
                [138.12950134277344, 303.2374267578125],
            ],
            [
                [184.17266845703125, 101.07916259765625],
                [184.17266845703125, 202.15826416015625],
                [230.2158203125, 303.2374267578125],
            ],
        ],
    },
    opacity: 1,
    pageIndex: 0,
    name: 'A167811E-6D10-4546-A147-B7AD775FE8AC',
    strokeColor: '#DC143C',
    type: 'pspdfkit/ink',
    v: 1,
};
Information

The bbox array contains the size and positional data for the annotation’s bounding box.

  1. You can add ink annotations to your document by passing the above JSON to the addAnnotation(jsonAnnotation) function. Create a Button component, which will trigger the addAnnotation function when pressed:

<Button
	onPress={() => {
		const annotationJSONInk = {
			bbox: [
				89.586334228515625,
				98.5791015625,
				143.12948608398438,
				207.1583251953125,
			],
			isDrawnNaturally: false,
			lineWidth: 5,
			lines: {
				intensities: [
					[0.5, 0.5, 0.5],
					[0.5, 0.5, 0.5],
				],
				points: [
					[
						[92.086334228515625, 101.07916259765625],
						[92.086334228515625, 202.15826416015625],
						[138.12950134277344, 303.2374267578125],
					],
					[
						[184.17266845703125, 101.07916259765625],
						[184.17266845703125, 202.15826416015625],
						[230.2158203125, 303.2374267578125],
					],
				],
			},
			opacity: 1,
			pageIndex: 0,
			name: 'A167811E-6D10-4546-A147-B7AD775FE8AC',
			strokeColor: '#DC143C',
			type: 'pspdfkit/ink',
			v: 1,
		};
		this.pdfRef.current
			.addAnnotation(annotationJSONInk)
			.then((result) => {
				if (result) {
					alert('Annotation was successfully added.');
				} else {
					alert('Failed to add annotation.');
				}
			})
			.catch((error) => {
				alert(JSON.stringify(error));
			});
	}}
	title="Add Ink Annotation"
	accessibilityLabel="Add Ink Annotation"
/>
  1. Put it all together:

import React, { Component } from 'react';
import { Platform, Button } from 'react-native';
import PSPDFKitView from 'react-native-pspdfkit';

const DOCUMENT =
	Platform.OS === 'ios'
		? 'Document.pdf'
		: 'file:///android_asset/Document.pdf';

export default class PSPDFKitDemo extends Component {
	constructor(props) {
		super(props);
		this.pdfRef = React.createRef();
	}

	render() {
		return (
			<>
				<PSPDFKitView
					document={DOCUMENT}
					configuration={{
						showThumbnailBar: 'scrollable',
						pageTransition: 'scrollContinuous',
						scrollDirection: 'vertical',
					}}
					ref={this.pdfRef}
					fragmentTag="PDF1"
					style={{ flex: 1 }}
				/>

				<Button
					onPress={() => {
						const annotationJSONInk = {
							bbox: [
								89.586334228515625,
								98.5791015625,
								143.12948608398438,
								207.1583251953125,
							],
							isDrawnNaturally: false,
							lineWidth: 5,
							lines: {
								intensities: [
									[0.5, 0.5, 0.5],
									[0.5, 0.5, 0.5],
								],
								points: [
									[
										[92.086334228515625, 101.07916259765625],
										[92.086334228515625, 202.15826416015625],
										[138.12950134277344, 303.2374267578125],
									],
									[
										[184.17266845703125, 101.07916259765625],
										[184.17266845703125, 202.15826416015625],
										[230.2158203125, 303.2374267578125],
									],
								],
							},
							opacity: 1,
							pageIndex: 0,
							name: 'A167811E-6D10-4546-A147-B7AD775FE8AC',
							strokeColor: '#DC143C',
							type: 'pspdfkit/ink',
							v: 1,
						};
						this.pdfRef.current
							.addAnnotation(annotationJSONInk)
							.then((result) => {
								if (result) {
									alert('Annotation was successfully added.');
								} else {
									alert('Failed to add annotation.');
								}
							})
							.catch((error) => {
								alert(JSON.stringify(error));
							});
					}}
					title="Add Ink Annotation"
					accessibilityLabel="Add Ink Annotation"
				/>
			</>
		);
	}
}
Information

Replace the contents of App.js with the above for a working example.

  1. Launch the app:

npx react-native run-android

GIF showing the result of ‘npx react-native run-android’

npx react-native run-ios

GIF showing the result of ‘npx react-native run-ios’

Adding Text Annotations

  1. Similar to ink annotations, you’ll define the text annotation JSON first:

const annotationJSONText = {
  bbox: [
    89.586334228515625, 98.5791015625, 143.12948608398438,
    207.1583251953125,
  ],
  horizontalAlign: 'center',
  verticalAlign: 'center',
  isBold: true,
  text: 'Welcome to\nPSPDFKit',
  font: 'Helvetica',
  fontColor: '#DC143C',
  fontSize: 24.0,

  opacity: 1,
  pageIndex: 0,
  name: 'A167811E-6D10-4546-A147-B7AD775FE8AC',
  type: 'pspdfkit/text',
  v: 1,
};
  1. Create a Button component, which will trigger the addAnnotation(jsonAnnotation) function when pressed:

<Button
	onPress={() => {
		const annotationJSONText = {
			bbox: [
				89.586334228515625,
				98.5791015625,
				143.12948608398438,
				207.1583251953125,
			],
			horizontalAlign: 'center',
			verticalAlign: 'center',
			isBold: true,
			text: 'Welcome to\nPSPDFKit',
			font: 'Helvetica',
			fontColor: '#DC143C',
			fontSize: 24.0,

			opacity: 1,
			pageIndex: 0,
			name: 'A167811E-6D10-4546-A147-B7AD775FE8AC',
			type: 'pspdfkit/text',
			v: 1,
		};
		this.pdfRef.current
			.addAnnotation(annotationJSONText)
			.then((result) => {
				if (result) {
					alert('Annotation was successfully added.');
				} else {
					alert('Failed to add annotation.');
				}
			})
			.catch((error) => {
				alert(JSON.stringify(error));
			});
	}}
	title="Add Text Annotation"
	accessibilityLabel="Add Text Annotation"
/>
  1. In your App.js file, you’ll have:

import React, { Component } from 'react';
import { Platform, Button } from 'react-native';
import PSPDFKitView from 'react-native-pspdfkit';

const DOCUMENT =
	Platform.OS === 'ios'
		? 'Document.pdf'
		: 'file:///android_asset/Document.pdf';

export default class PSPDFKitDemo extends Component {
	constructor(props) {
		super(props);
		this.pdfRef = React.createRef();
	}

	render() {
		return (
			<>
				<PSPDFKitView
					document={DOCUMENT}
					configuration={{
						showThumbnailBar: 'scrollable',
						pageTransition: 'scrollContinuous',
						scrollDirection: 'vertical',
					}}
					ref={this.pdfRef}
					fragmentTag="PDF1"
					style={{ flex: 1 }}
				/>

				<Button
					onPress={() => {
						const annotationJSONInk = {
							bbox: [
								89.586334228515625,
								98.5791015625,
								143.12948608398438,
								207.1583251953125,
							],
							isDrawnNaturally: false,
							lineWidth: 5,
							lines: {
								intensities: [
									[0.5, 0.5, 0.5],
									[0.5, 0.5, 0.5],
								],
								points: [
									[
										[92.086334228515625, 101.07916259765625],
										[92.086334228515625, 202.15826416015625],
										[138.12950134277344, 303.2374267578125],
									],
									[
										[184.17266845703125, 101.07916259765625],
										[184.17266845703125, 202.15826416015625],
										[230.2158203125, 303.2374267578125],
									],
								],
							},
							opacity: 1,
							pageIndex: 0,
							name: 'A167811E-6D10-4546-A147-B7AD775FE8AC',
							strokeColor: '#DC143C',
							type: 'pspdfkit/ink',
							v: 1,
						};
						this.pdfRef.current
							.addAnnotation(annotationJSONInk)
							.then((result) => {
								if (result) {
									alert('Annotation was successfully added.');
								} else {
									alert('Failed to add annotation.');
								}
							})
							.catch((error) => {
								alert(JSON.stringify(error));
							});
					}}
					title="Add Ink Annotation"
					accessibilityLabel="Add Ink Annotation"
				/>

				<Button
					onPress={() => {
						const annotationJSONText = {
							bbox: [
								89.586334228515625,
								98.5791015625,
								143.12948608398438,
								207.1583251953125,
							],
							horizontalAlign: 'center',
							verticalAlign: 'center',
							isBold: true,
							text: 'Welcome to\nPSPDFKit',
							font: 'Helvetica',
							fontColor: '#DC143C',
							fontSize: 24.0,

							opacity: 1,
							pageIndex: 0,
							name: 'A167811E-6D10-4546-A147-B7AD775FE8AC',
							type: 'pspdfkit/text',
							v: 1,
						};
						this.pdfRef.current
							.addAnnotation(annotationJSONText)
							.then((result) => {
								if (result) {
									alert('Annotation was successfully added.');
								} else {
									alert('Failed to add annotation.');
								}
							})
							.catch((error) => {
								alert(JSON.stringify(error));
							});
					}}
					title="Add Text Annotation"
					accessibilityLabel="Add Text Annotation"
				/>
			</>
		);
	}
}
Information

Replace the contents of App.js with the above for a working example.

  1. Launch your app:

npx react-native run-android
npx react-native run-ios

GIF showing the result of ‘npx react-native run-ios’

Setting an Annotation Author

Programmatically adding an author name is accomplished by passing the annotationAuthorName prop in the PSPDFKitView component:

<PSPDFKitView
  document={DOCUMENT}
+  annotationAuthorName={'Jane Appleseed'}
  configuration={{
    showThumbnailBar: 'scrollable',
    pageTransition: 'scrollContinuous',
    scrollDirection: 'vertical',
  }}
  ref={this.pdfRef}
  fragmentTag="PDF1"
  style={{flex: 1}}
/>

Embedding Annotations into a PDF

Before creating a new document with embedded annotations, it’s necessary to save all the annotations in the current document first. This can be done by calling the saveCurrentDocument() function.

After a successful save, call the PSPDFKit.processAnnotations(annotationChange, annotationType, sourceDocumentPath, processedDocumentPath) function to create a new document with embedded annotations.

Once again, you’ll create a Button component, which will trigger the above steps. Don’t forget to import the react-native-fs and NativeModules before continuing:

import React, {Component} from 'react';
- import {Platform, Button} from 'react-native';
+ import {Platform, Button, NativeModules} from 'react-native';
import PSPDFKitView from 'react-native-pspdfkit';
+ import RNFS from 'react-native-fs';

const DOCUMENT =
  Platform.OS === 'ios' ? 'Document.pdf' : 'file:///android_asset/Document.pdf';

+ const {PSPDFKit} = NativeModules;

...
<Button
	onPress={async () => {
		const processedDocumentPath =
			RNFS.DocumentDirectoryPath + '/flattened.pdf';
		// Delete the processed document if it already exists.
		RNFS.exists(processedDocumentPath)
			.then((exists) => {
				if (exists) {
					RNFS.unlink(processedDocumentPath);
				}
			})
			.then(() => {
				// First, save all annotations in the current document.
				this.pdfRef.current
					.saveCurrentDocument()
					.then((success) => {
						if (success) {
							console.log(PSPDFKit);
							// Then, embed all the annotations.
							PSPDFKit.processAnnotations(
								'embed',
								'all',
								DOCUMENT,
								processedDocumentPath,
							)
								.then((success) => {
									if (success) {
										// And finally, present the newly processed document with embedded annotations.
										PSPDFKit.present(
											processedDocumentPath,
											{},
										);
									} else {
										alert('Failed to embed annotations.');
									}
								})
								.catch((error) => {
									alert(JSON.stringify(error));
								});
						} else {
							alert('Failed to save current document.');
						}
					});
			});
	}}
	title="Embed All Annotations"
/>

You’ll have the following in your App.js:

import React, { Component } from 'react';
import { Platform, Button, NativeModules } from 'react-native';
import PSPDFKitView from 'react-native-pspdfkit';
import RNFS from 'react-native-fs';

const DOCUMENT =
	Platform.OS === 'ios'
		? 'Document.pdf'
		: 'file:///android_asset/Document.pdf';

const { PSPDFKit } = NativeModules;

export default class PSPDFKitDemo extends Component {
	constructor(props) {
		super(props);
		this.pdfRef = React.createRef();
	}

	render() {
		return (
			<>
				<PSPDFKitView
					document={DOCUMENT}
					annotationAuthorName={'Jane Appleseed'}
					configuration={{
						showThumbnailBar: 'scrollable',
						pageTransition: 'scrollContinuous',
						scrollDirection: 'vertical',
					}}
					ref={this.pdfRef}
					fragmentTag="PDF1"
					style={{ flex: 1 }}
				/>

				<Button
					onPress={() => {
						const annotationJSONInk = {
							bbox: [
								89.586334228515625,
								98.5791015625,
								143.12948608398438,
								207.1583251953125,
							],
							isDrawnNaturally: false,
							lineWidth: 5,
							lines: {
								intensities: [
									[0.5, 0.5, 0.5],
									[0.5, 0.5, 0.5],
								],
								points: [
									[
										[92.086334228515625, 101.07916259765625],
										[92.086334228515625, 202.15826416015625],
										[138.12950134277344, 303.2374267578125],
									],
									[
										[184.17266845703125, 101.07916259765625],
										[184.17266845703125, 202.15826416015625],
										[230.2158203125, 303.2374267578125],
									],
								],
							},
							opacity: 1,
							pageIndex: 0,
							name: 'A167811E-6D10-4546-A147-B7AD775FE8AC',
							strokeColor: '#DC143C',
							type: 'pspdfkit/ink',
							v: 1,
						};
						this.pdfRef.current
							.addAnnotation(annotationJSONInk)
							.then((result) => {
								if (result) {
									alert('Annotation was successfully added.');
								} else {
									alert('Failed to add annotation.');
								}
							})
							.catch((error) => {
								alert(JSON.stringify(error));
							});
					}}
					title="Add Ink Annotation"
					accessibilityLabel="Add Ink Annotation"
				/>

				<Button
					onPress={() => {
						const annotationJSONText = {
							bbox: [
								89.586334228515625,
								98.5791015625,
								143.12948608398438,
								207.1583251953125,
							],
							horizontalAlign: 'center',
							verticalAlign: 'center',
							isBold: true,
							text: 'Welcome to\nPSPDFKit',
							font: 'Helvetica',
							fontColor: '#DC143C',
							fontSize: 24.0,

							opacity: 1,
							pageIndex: 0,
							name: 'A167811E-6D10-4546-A147-B7AD775FE8AC',
							type: 'pspdfkit/text',
							v: 1,
						};
						this.pdfRef.current
							.addAnnotation(annotationJSONText)
							.then((result) => {
								if (result) {
									alert('Annotation was successfully added.');
								} else {
									alert('Failed to add annotation.');
								}
							})
							.catch((error) => {
								alert(JSON.stringify(error));
							});
					}}
					title="Add Text Annotation"
					accessibilityLabel="Add Text Annotation"
				/>

				<Button
					onPress={async () => {
						const processedDocumentPath =
							RNFS.DocumentDirectoryPath + '/flattened.pdf';
						// Delete the processed document if it already exists.
						RNFS.exists(processedDocumentPath)
							.then((exists) => {
								if (exists) {
									RNFS.unlink(processedDocumentPath);
								}
							})
							.then(() => {
								// First, save all annotations in the current document.
								this.pdfRef.current
									.saveCurrentDocument()
									.then((success) => {
										if (success) {
											// Then, embed all the annotations.
											PSPDFKit.processAnnotations(
												'embed',
												'all',
												DOCUMENT,
												processedDocumentPath,
											)
												.then((success) => {
													if (success) {
														// And finally, present the newly processed document with embedded annotations.
														PSPDFKit.present(
															processedDocumentPath,
															{},
														);
													} else {
														alert(
															'Failed to embed annotations.',
														);
													}
												})
												.catch((error) => {
													alert(JSON.stringify(error));
												});
										} else {
											alert(
												'Failed to save current document.',
											);
										}
									});
							});
					}}
					title="Embed All Annotations"
				/>
			</>
		);
	}
}
Information

Replace the contents of App.js with the above for a working example.

Launch your app:

npx react-native run-android

GIF showing the result of ‘npx react-native run-ios’

npx react-native run-ios

GIF showing the result of ‘npx react-native run-ios’

Conclusion

In this post, you learned how to annotate PDFs and embed annotations into a PDF in React Native using PSPDFKit. In case of any hiccups, don’t hesitate to reach out to our Support team for help.

PSPDFKit for React Native is an SDK for viewing, annotating, and editing PDFs. It offers developers the ability to quickly add PDF functionality to any React Native application. Try it for free, or visit our demo to see it in action.

Related Products
PSPDFKit for React Native

Product Page
Guides
Example Projects

Share Post
Free 60-Day Trial Try PSPDFKit in your app today.
Free Trial

Related Articles

Explore more
PRODUCTS  |  React Native • Releases

PSPDFKit 2.9 for React Native Adds Main Toolbar Customization

PRODUCTS  |  React Native • Releases

PSPDFKit 2.8 for React Native Adds TypeScript Support

CUSTOMER STORIES  |  Case Study • React Native • iOS • Android

Case Study: How Trinoor Uses PSPDFKit to Drive Operational Excellence with Flexible, Mobile Applications for the Energy and Utilities Market