Add a Media Editor to Your Web App with Cloudinary

Ifeoma Imoh

Cloudinary provides a Media Editor, which is an interactive user interface that provides users on your website with common editing actions, reducing dependency on designers for simple recurring tasks.

In this post, we will build a demo application that shows how to use the Cloudinary Media Editor widget to perform image editing actions like cropping and resizing images, adding image and text overlay, e.t.c.

Here is a link to the demo on CodeSandbox.

Setting Up the Project

To create a Next.js application, navigate to the project directory of your choice and run the following command:

1npx create-next-app cloudinary-media-editor-demo

Now we can start our application on http://localhost:3000/ using the following command:

1npm run dev

Setting up Cloudinary

First, sign up for a free Cloudinary account if you don’t have one already. Important details are displayed on your account’s Management Console (aka Dashboard): your cloud name, API key, etc.

Next, we need to create some environment variables to hold our Cloudinary details. We will be sending images to Cloudinary via unsigned POST requests, and to do this, we need our account cloud name and an unsigned upload preset. Create a new file called .env at the root of the project and add the following to it:

1NEXT_PUBLIC_CLOUD_NAME = "INSERT YOUR CLOUD NAME HERE";
2NEXT_PUBLIC_UPLOAD_PRESET = "INSERT YOUR UNSIGNED UPLOAD PRESET KEY HERE";

This will be used as a default when the project is set up on another system. To update your local environment, create a copy of the .env file using this command:

1cp .env .env.local

By default, this local file is added to .gitignore and mitigates the security risk of inadvertently exposing secret credentials to the public. You can update .env.local with your cloud name and generated upload preset.

Create a new folder named utils at the root of your project. This folder will hold all our utility functions. In the utils folder, create a file called cloudinaryConfig.js. This file will give access to the environment variables and prevent repeated process.env. calls throughout the project. Add the following to your cloudinaryConfig.js file:

1export const cloudName = process.env.NEXT_PUBLIC_CLOUD_NAME;
2export const uploadPreset = process.env.NEXT_PUBLIC_UPLOAD_PRESET;

Building the Frontend

Let's create a component that allows a user to select an image to edit from their computer. We need the public ID of the selected image to configure the Media Editor widget, so the image has to be uploaded to Cloudinary.

Add the following to your index.js file:

1import Script from "next/script";
2import styles from "../styles/Home.module.css";
3import { cloudName, uploadPreset } from "../utils/cloudinaryConfig";
4
5export default function Home() {
6 const handleUpload = async (e) => {
7 const clUrl = `https://api.cloudinary.com/v1_1/${cloudName}/image/upload`;
8 const file = e.target.files[0];
9 const formData = new FormData();
10 formData.append("file", file);
11 formData.append("upload_preset", uploadPreset);
12 const res = await fetch(clUrl, {
13 method: "POST",
14 body: formData,
15 });
16 const data = await res.json();
17 const publicId = data.public_id;
18 console.log(publicId);
19 };
20
21 return (
22 <div className={styles.container}>
23 <input type="file" accept="image/*" onChange={handleUpload} />
24 <Script
25 src="https://media-editor.cloudinary.com/all.js"
26 type="text/javascript"
27 ></Script>
28 </div>
29 );
30}

In the code above, we use Next.js Script tag to include a remote JavaScript file that contains all the Media Editor functionality. The Home component renders a file input with an onChange event. When the event is called, it triggers the handleUpload function. In the handleUpload function, we are making a POST request to the Cloudinary upload endpoint. The request body is a FormData object containing the selected file and the upload preset we defined. When the request is completed, we parse the response to JSON. Finally, we extract the public ID of the uploaded image from the data object, which will be used to configure the Media Editor widget.

Initialize and Update Media Editor Widget

So far, we have included the remote JavaScript file that brings the media editor functionality to our site. Next, we need to initialize and update the Media Editor configurations. In the utils folder, create a new file named startEditing.js and add the following to it:

1import { cloudName } from "./cloudinaryConfig";
2
3function startEditing(publicId) {
4 const myEditor = cloudinary.mediaEditor();
5
6 myEditor.update({
7 cloudName: cloudName,
8 publicIds: [publicId],
9 });
10 myEditor.show();
11}
12
13export default startEditing;

The function above accepts publicId as props, which will be the public ID of the uploaded image. In the function, we start by creating and initializing the Media Editor widget with the cloudinary.mediaEditor() method. Then using the update method, we configure the widget with an object containing some configuration parameters — our cloud name and the public ID of the image to edit.

These are the only required parameters (options); the others are optional. Finally, we call the show method to display the initialized widget. See here for a complete list of parameters available for configuring the Media Editor widget.

Now let’s import this file in our index.js file. Add this line to the top of your pages/index.js file:

1import startEditing from "../utils/startEditing";

Then add this to the bottom of the handleUpload function:

1const handleUpload = async (e) => {
2 //…
3 startEditing(publicId);
4};

Here we’re calling the startEditing function and passing the public ID of the image uploaded by the user. This open’s up the Media Editor widget with the image for editing.

Now run npm start in the project folder, open http://localhost:3000 in your browser, and select an image to be uploaded.

You should see the selected image uploaded and the Media Editor populated with the image. Neat right?

Configuring the Image Widget

The media editor can be configured for both images and videos; however, we will only look at image configuration in this post. To set up the Media Editor for image editing, we must pass the image editing parameters to an image object parameter, as shown below:

1import { cloudName } from "./cloudinaryConfig";
2
3function startEditing(publicId) {
4 const myEditor = cloudinary.mediaEditor();
5 myEditor.update({
6 cloudName: cloudName,
7 publicIds: [publicId],
8 image: {
9 steps: ["resizeAndCrop", "imageOverlay", "textOverlays", "export"],
10 },
11 });
12 myEditor.show();
13}
14
15export default startEditing;

In the code above, we passed the steps parameter, containing the steps we want to include in our image widget, to an image object parameter. The steps defined above are the only options and will be displayed in the order specified in the steps parameter. The export step should always be the last.

Next, within the image object, we need to define the configuration for each step we want to include in the Media Editor Widget.

resizeAndCrop Parameter

With the resizeAndCrop parameter, we can populate the Media Editor with an array of presets from which the user can choose to resize and crop the image. Cloudinary provides some predefined presets included by default and some predefined shortcuts that we can include.

Add the following to the image object parameter:

1resizeAndCrop: {
2 flip: true,
3 rotate: true,
4 presets: [
5 "original",
6 "square",
7 "landscape-16:9",
8 "landscape-4:3",
9 "portrait-3:4",
10 "portrait-9:16",
11 "facebookAd",
12 "facebookCover",
13 "instagramStory",
14 "twitterAd",
15 "linkedInAd",
16 "linkedInCover",
17 { label: "Cover Ad", width: 500, height: 1000 },
18 ],
19 },

In the code above, we added a few predefined presets (Twitter Ad and LinkedIn Ad, e.t.c )and one custom preset named Cover Ad. We defined the custom preset by specifying the label, width, and height properties. We also specified the flip and rotate parameters, whose values are set to true to display the flip and rotate buttons to enable flipping and rotating the image.

Save the changes and open your browser to test the application.

You should be able to select from the predefined options we specified and also crop the image manually using the crop handles. See the ResizeProps options available for the resizeAndCrop parameter.

imageOverlay Parameter

We can use the imageOverlay parameter to populate the Media Editor with an array of images from which a user can choose to add to the base image. Let’s add one Cloudinary logo as an overlay option.

Add the following to the image object parameter below the resizeAndCrop parameter:

1imageOverlay: {
2 overlays: [
3 {
4 publicId: "logo",
5 label: "Logo",
6 placementOptions: [
7 "right",
8 "left",
9 "top",
10 "bottom",
11 "top_left",
12 "top_right",
13 "bottom_left",
14 "bottom_right",
15 "middle",
16 ],
17 },
18 ],
19 },

In the code above, we specified an overlay option in the array with a publicId, a label, and an array of placementOptions. We are using predefined placement options, but you can also define custom placement locations. Each placement option is defined by a bounding box (width and height), a location on the base image (gravity), and any offset from the selected location (x and y).

Now, save the changes and open your browser to test the application.

You should be able to select an image from the different placement options we specified to add to the base image. See the ImageOverlayProps options available for the imageOverlay parameter.

textOverlays Parameter

Specifying the textOverlay parameter will enable us to add text to the base image that can be scaled, edited, moved to any location within the image, e.t.c, and it accepts the following properties:

  • The fonts property is used to define an array of allowed fonts and defaults to all the built-in fonts.
  • The presets property is used to define an array of text overlay presets available for the user to select, and it defaults to: ["heading", "body", "subtitle", "caption"].
  • The guidelinesUrl property can be used to add an informative image with overlay instructions.
  • The initialColors property is used to define an array of colors for the user to select. Default: ["#ffffff", "#000000"]
  • The showColorPicker is a boolean property that indicates if the color picker is available for the user to select a color, and it defaults to true.

Add the following to the image object parameter below the imageOverlay parameter:

1textOverlays: {
2 presets: [
3 "heading",
4 "subtitle",
5 "body",
6 "caption",
7 {
8 label: "My Header",
9 size: 100,
10 font: "Helvetica",
11 previewText: "ABC",
12 weight: "bold",
13 style: "italic",
14 color: "#ffffff",
15 },
16 ],
17 initialColors: [
18 "#3448c5",
19 "#ff5050",
20 "#f7bc00",
21 " #48c4d8",
22 "#0052cc",
23 "#a600cc",
24 "#8ecc00",
25 ],
26 },

In the code above, we didn't specify the fonts property because we will be using the default built-in fonts, but you can also define some custom fonts if you wish. We specified some predefined text presets and defined one custom text preset named My Header. We also specified some initial color options.

You can save the changes now and open your browser to test the application.

See the TextOverlaysProps for options available for the textOverlay parameter.

export Parameter

Specifying the widget's export step allows your users to select and export from the provided exporting options.

Add the following to the image object parameter below the textOverlay parameter:

1export: {
2 formats: ["auto", "png", "webp"],
3 quality: ["auto", "best", "good", 55, 75, "low"],
4 download: true,
5 share: true,
6 },

Using the export parameter, we specified an array of formats and quality with which we would like to populate the Media Editor. We also specified the download property and set its values to true, indicating that we want the download button to be displayed. See the ExportProps options available for the export parameter.

Save the changes again and test the application in your browser.

You should be able to select and download any of the different exporting options provided.

Find the complete project here on GitHub.

Conclusion

Image editing is important in marketing or branding because properly edited images represent your brand. Cloudinary provides a Media Editor, an interactive user interface that offers users on your website with common editing actions. In this post, we built a demo application that shows how to configure and use the Media Editor widget.

Here are some resources you may find helpful:

Ifeoma Imoh

Software Developer

Ifeoma is a software developer and technical content creator in love with all things JavaScript.