Crop An Image Before Upload With React
- Publish date
In this short tutorial we'll learn how to automatically crop images before they're uploaded using React and Pintura.
We'll start from scratch with a blank project, if you already have a project you want to add an image cropper component, then you can skip to Installing Pintura Image Editor
Let's get started!
Creating A React Project
We'll type the following in our terminal to create a new React project
npx create-next-app@latest
We'll press return a couple times to choose default answers to the tech stack questions. This will start the installation process. We'll assume you've called the app my-app
⠹ Installing packages (npm)...
That'll run for a couple seconds until it shows.
Success! Created my-app
Let's navigate to the my-app
folder.
cd my-app
We can now install Pintura.
Installing Pintura Image Editor
Let's install the Pintura component, this will make it super easy to build our image crop functionality.
npm install @pqina/pintura
Now we can start the development server by typing:
npm run dev
It'll show the following message if all is fine.
✓ Ready in 3s
Let's open the project in our browser at http://localhost:3000/
, it'll show the default React project page, our next step is altering this page so it shows a file input so we can receive user data.
Crop images before upload with Pintura
We first create a component
directory and add a file called CropInput.tsx
Add the following code to the file.
// 1. This means this component will only run in the browser
'use client';
// 2. We'll return a basic file input element for now
export default function CropInput() {
return <input type="file" />;
}
Now we'll open the app/page.tsx
page and will replace all contents so it looks like this:
// 1. We need to get the CropInput
import CropInput from './component/CropInput';
export default function Home() {
return (
<div>
{/* 2. Add CropInput component */}
<CropInput></CropInput>
</div>
);
}
Let's go back to the CropInput.tsx
file and update our file input field so we can listen for changes.
// 1. Import the ChangeEvent type from React so we can type the event
import { ChangeEvent } from 'react';
// 2. Add this function to handle changes
function handleFileChange(e: ChangeEvent<HTMLInputElement>) {
/* Deal with file changes here */
}
export default function Home() {
return (
<div>
{/* 3. Add accept attribute to filter images and onChange handler to listen to change events */}
<input type="file" accept="image/*" onChange={handleFileChange} />
</div>
);
}
When the file input value is changed the handleFileChange
function runs, lets implement the function now.
function handleFileChange(e: ChangeEvent<HTMLInputElement>) {
// 1. Check if we got files to work with
const fileInput = e.target;
if (!fileInput || !fileInput.files || fileInput.files.length === 0) return;
// 2. Check if a file is present and if it is image (just to be sure)
const file = fileInput.files[0];
if (!file || !file.type.startsWith('image')) return;
// We now have a file to work with
}
Let's edit the file with Pintura.
import { ChangeEvent } from 'react';
// 1. Import the Pintura modal factory
import { openDefaultEditor } from '@pqina/pintura';
// 2. Import the Pintura styles
import '@pqina/pintura/pintura.css';
function handleFileChange(e: ChangeEvent<HTMLInputElement>) {
// Check if we got files to work with
const fileInput = e.target;
if (!fileInput || !fileInput.files || fileInput.files.length === 0) return;
// Check if a file is present and if is image
const file = fileInput.files[0];
if (!file || !file.type.startsWith('image')) return;
// 3. Open the user image with Pintura
const editor = openDefaultEditor({
// 4. We'll set the user file as source
src: file,
// 5. We want a square crop
imageCropAspectRatio: 1,
});
}
Okay, now if we select an image the editor will open, but after editing we also need to store the file.
We can do so by updating the input files
property. Alternatively you can upload the resulting file with fetch
or XMLHttpRequest
, see this article on uploading images with NodeJS.
import { ChangeEvent } from 'react';
import { openDefaultEditor } from '@pqina/pintura';
import '@pqina/pintura/pintura.css';
function handleFileChange(e: ChangeEvent<HTMLInputElement>) {
// Check if we got files to work with
const fileInput = e.target;
if (!fileInput || !fileInput.files || fileInput.files.length === 0) return;
// Check if a file is present and if is image
const file = fileInput.files[0];
if (!file || !file.type.startsWith('image')) return;
// Open the user image with Pintura
const editor = openDefaultEditor({
src: file,
imageCropAspectRatio: 1,
});
// 1. We listen for the resulting image
editor.on('process', ({ dest }) => {
// 2. Add our `dest` (the output File) to a DataTransfer so we can then get a FileList
const dataTransfer = new DataTransfer();
dataTransfer.items.add(dest);
// 3. Update the file input with the new files list
fileInput.files = dataTransfer.files;
});
}
Now if you read out the files list you can upload the resulting file to a server of your choosing.
We're done!
That's it! I hope you found this an interesting read, feel free to reach out with any questions.
You can find a Pintura React example project on GitHub it includes a link to a live code environment where you can run the project online.