Blog Post

Implement Drag and Drop for PSPDFKit in a React Application

Illustration: Implement Drag and Drop for PSPDFKit in a React Application

In this tutorial, you’ll implement a drag-and-drop example using React and our Web PDF SDK. This will allow you to drag an image onto a PDF page and add the image as an image annotation.

Getting Started

You’ll use Next.js to get started. If you aren’t familiar with Next.js, you can learn about it by reading the official documentation.

First, you’ll scaffold the Next.js application and install the necessary dependencies:

npx create-next-app@latest
npm install --save pspdfkit

Integrating PSPDFKit

To integrate PSPDFKit, first move the static files needed by PSPDFKit to the public folder:

cp -R ./node_modules/pspdfkit/dist/ ./public/

Once this is done, use dynamic import to import PSPDFKit lazily after the component has mounted on the client site. You’ll do this for two reasons:

  • To avoid loading PSPDFKit on the server side. As PSPDFKit only works on the client side, bundling it with the application isn’t a good idea.

  • To load it only when required to make sure that time to first paint isn’t affected by it.

Here’s the code:

import React, { useEffect, useRef } from 'react';

export default function App() {
	const containerRef = useRef(null);
	const PSPDFKit = useRef(null);

	useEffect(() => {
		const container = containerRef.current;

		(async function () {
			PSPDFKit.current = await import('pspdfkit');
			instance.current = await PSPDFKit.current.load({
				container,
				document: '/example.pdf',
				baseUrl: `${window.location.protocol}//${window.location.host}/`,
				theme: PSPDFKit.current.Theme.DARK,
			});
		})();

		return () => PSPDFKit.current?.unload(container);
	}, []);

	return <div ref={containerRef} style={{ height: '100vh' }} />;
}

If you open http://localhost:3000, you’ll see example.pdf loaded on the screen. Next, add an image that needs to be dragged to the PDF document. You can add more images based on the same logic.

Implementing Drag and Drop

You’ll use the native drag-and-drop APIs to implement the functionality. The draggable images should adhere to the following constraints:

  • The img tag should have a draggable prop set to true.

  • The img tag should handle the onDragStart event.

const handleDragStart = useCallback(
	(ev) => ev.dataTransfer.setData('text/plain', ev.target.src),
	[],
);

return (
	//...
	<img
		src="image.png"
		draggable="true"
		onDragStart={handleDragStart}
	/>
	//...
);

In the code above, you handled the drag start event. Now you have to handle the onDrop event. The onDrop callback gets an event containing the coordinates of the drop point and the data that was set in the onDragStart callback. You can access that data using evebet.dataTransfer.getData("text/plain"):

const handleDrop = (event) => {
	ev.preventDefault();

	const imgSrc = ev.dataTransfer.getData('text/plain');

	// The coordinates of the drop point relative to the entire window.
	console.log(event.clientX, event.clientY);
};

return (
	//...
	<div onDrop={handleDrop} onDragOver={(ev) => ev.preventDefault()}>
		<div ref={containerRef} style={{ height: '100vh' }} />
	</div>
	//...
);

Once you get the drop coordinates, you’ll have to convert the coordinates in the page space to get the position of the drop point inside the PDF and create an image annotation. You can use Instance#transformClientToPageSpace to do that.

After you have the transformed coordinates, you can use the current page index and the image src to add an image annotation to the PDF:

const handleDrop = useCallback(
	(ev) => {
		(async function () {
			ev.preventDefault();
			// Get the ID of the target and add the moved element to the target's DOM.
			const img = ev.dataTransfer.getData('text/plain');

			const pointInPage = await instance.current.transformClientToPageSpace(
				new PSPDFKit.current.Geometry.Point({
					x: ev.clientX,
					y: ev.clientY,
				}),
				instance.current.viewState.currentPageIndex,
			);

			// Generate a blob from the image URL.
			const image = await fetch(img);
			const blob = await image.blob();

			const imageAttachmentId = await instance.current.createAttachment(
				blob,
			);

			// Create an image annotation from all the above details.
			const annotation = new PSPDFKit.current.Annotations.ImageAnnotation(
				{
					pageIndex: instance.current.viewState.currentPageIndex,
					contentType: 'image/jpeg',
					imageAttachmentId,
					description: 'Example Image Annotation',
					boundingBox: new PSPDFKit.current.Geometry.Rect({
						left: pointInPage.x,
						top: pointInPage.y,
						width: 200,
						height: 135,
					}),
				},
			);

			await instance.current.create(annotation);
		})();
	},
	[instance.current, PSPDFKit.current],
);

The above drop callback will create an image annotation at the same place where you dragged the image on the PDF. If you want to change the image’s alignment, you can adjust the left and top positions that you passed to the image bounding box.

Conclusion

This blog explains the essential code blocks that will help you implement drag and drop in your React application. Please check out this GitHub repository to see the complete working code. You can interact with the live drag-and-drop demo for images with PSPDFKit below or in the Catalog example.

PSPDFKit offers a commercial, feature-rich, and completely customizable React PDF library that’s easy to integrate and comes with well-documented APIs to handle advanced use cases. Try it for free, or visit our demo to see it in action.

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

Related Articles

Explore more
TUTORIALS  |  Web • JavaScript • How To • PDF Viewer

How to Build a JavaScript Image Viewer with Viewer.js

PRODUCTS  |  Web • Releases • Components

PSPDFKit for Web 2022.4 Renders Media Annotations

TUTORIALS  |  Web • Remix • React • JavaScript • How To • PDF Viewer

How to Build a PDF Viewer with Remix and PSPDFKit