Implement drag and drop for Nutrient 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
Setting up the public folder
Create a public
folder in the root directory of your Next.js project and add an example.pdf
file inside the folder. This is where Nutrient will load the PDF file from:
mkdir public cp example.pdf ./public/
Integrating Nutrient
To integrate Nutrient, first move the static files needed by Nutrient to the public
folder:
cp -R ./node_modules/pspdfkit/dist/ ./public/
Once this is done, use dynamic imports with "use client"
to import Nutrient lazily after the component has mounted on the client site. You’ll do this for two reasons:
-
To avoid loading Nutrient on the server side. As Nutrient 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.
Add the following code in src/app/page.js
:
// src/app/page.js 'use client'; import { 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 adraggable
prop set totrue
. -
The
img
tag should handle theonDragStart
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 Nutrient below or in the Catalog example.
Nutrient 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.
FAQ
Here are a few frequently asked questions about using drag and drop.
What is the purpose of using drag and drop in a PDF application?
Drag and drop functionality allows users to easily add images as annotations to a PDF document, enhancing the interactivity of the application.
Which JavaScript framework is recommended for implementing Nutrient?
This post recommends using Next.js, a React framework, to efficiently integrate Nutrient and manage application routing.
How can I handle dropped images in my application?
You can handle dropped images by using the onDrop
event to access the image source and transform the drop coordinates to place the annotation correctly.
What do I need to install before integrating Nutrient into my project?
You need to install the Nutrient package, along with any dependencies required for your React application, such as Next.js.
Can I customize the appearance of the images added to the PDF?
Yes, you can customize the bounding box of the image annotation by adjusting the left
and top
properties when creating the annotation.