How to Upload Images in Vue.js with Drag N Drop

Banner for a MediaJam post

Ashutosh K Singh


According to the Stack Overflow 2020 survey, Vue.js was one of the developer's Most Loved Web frameworks. In this media jam, we will see how to upload an image to Cloudinary in Vue.js with a drag and drop operation.

If you want to jump right into the code, check out the GitHub Repo here.



In this tutorial, you will use Vue CLI to create the initial Vue.js project. Run the following command to install the Vue CLI.

1npm install -g @vue/cli

Now, create a Vue.js project named vue-image-upload by running the following command in the terminal.

1vue create vue-image-upload

When prompted to choose the preset, the Default (Vue 3) ([Vue 3] babel, eslint) preset.

1Vue CLI v4.5.13
2? Please pick a preset:
3 Default ([Vue 2] babel, eslint)
4> Default (Vue 3) ([Vue 3] babel, eslint)
5 Manually select features

After the project has been created, run the following command to start the development server.

1cd vue-image-upload
2npm run serve

Navigate to http://localhost:8080/ in your browser, here is how your app will look like.

Vue.js Initial App

Update the src/App.vue file to clean the sample code like this.

2 <h1>Vue Image Upload</h1>
6export default {
7 name: "App",
8 components: {},
13#app {
14 font-family: Avenir, Helvetica, Arial, sans-serif;
15 -webkit-font-smoothing: antialiased;
16 -moz-osx-font-smoothing: grayscale;
17 text-align: center;
18 color: #2c3e50;
19 margin-top: 60px;

Here is how your app will look like now.

Cleaned Sample Code

In this tutorial, you will upload the image with a POST request to the Cloudinary REST API. For this, you will need to create an unsigned preset to upload without any authentication signature.

Head over to Settings → Upload → Upload presets on your Cloudinary dashboard and create an unsigned preset if one doesn't already exist and copy it.

Cloudinary Settings

Create a file named .env by running the following command to store your Cloudinary cloud name and Upload Preset securely.

1touch .env

Paste the cloud name and upload preset to the .env file.


How To Drag N Drop Images in Vue.js

In this section, you will build the dropzone for dragging and dropping the images. You will use HTML5 Drag and Drop API to make this dropzone.

Update App.vue file like this.

2 <h1>Vue Image Upload</h1>
4 <div
5 class="dropzone"
6 @dragover.prevent
7 @dragenter.prevent
8 @dragstart.prevent
9 @drop.prevent="handleFileChange($event.dataTransfer)"
10 >
11 <input
12 id="file-input"
13 type="file"
14 accept="image/png, image/jpeg"
15 @change="handleFileChange($"
16 required
17 />
18 <h2 for="file-input">Click or Drag N Drop Image</h2>
19 <img v-bind:src="preview" />
20 <h3 v-if="preview">File name: {{ fileName }}</h3>
21 </div>
23 <button type="submit" v-on:click="upload">Upload</button>
27export default {
28 name: "App",
29 data() {
30 return {
31 fileName: "",
32 preview: null,
33 preset: process.env.VUE_APP_UPLOAD_PRESET,
34 formData: null,
35 cloudName: process.env.VUE_APP_CLOUD_NAME,
36 success: "",
37 };
38 },
39 methods: {
40 handleFileChange: function (event) {
41 this.file = event.files[0];
42 this.fileName =;
44 this.formData = new FormData();
45 this.formData.append("upload_preset", this.preset);
47 let reader = new FileReader();
48 reader.readAsDataURL(this.file);
50 reader.onload = (e) => {
51 this.preview =;
52 this.formData.append("file", this.preview);
53 };
54 },
55 },

In the above code, you create a dropzone for dropping the images using @dragover, @dragenter, @dragstart, and @drop event handlers. The function handleFileChange() is triggered when selecting an image either by drag and drop operation or using the input element.

If an image is dropped in the dropzone, the DataTransfer object is passed to the handleFileChange() function. If a user selects an image using the input element, the target object is passed to the handleFileChange() function.

This function uses FileReader API to read the contents of the image and convert it to a data URL from a File Object. File objects are obtained either from a FileList object returned due to a user selecting files using the <input> element or from a drag and drop operation's DataTransfer object.

The FileReader API converts the image to a Data URL using readAsDataURL() and onload() functions and is stored inside the preview variable. You can read more about readAsDataURL() here.

You also store the name of the image in a variable named fileName. Both the name and the image selected are displayed to the user using the v-if directive. You can read more about this directive here.

The data URL of the image or preview variable is appended to a FormData along with the upload_preset. This formData is sent to the API in the request body when the user clicks the Upload button triggering the upload() function. You will create the upload() function in the next section.

Here is how your app will look like.

App without Styles

Here is how your app looks after selecting or dragging/dropping an image. You can see the chosen image and its name on the app with the help of the fileName and preview variables.

App without styles with Image seleccted

You can style the app by copying and pasting the following code to the style section.

2#app {
3 font-family: Avenir, Helvetica, Arial, sans-serif;
4 -webkit-font-smoothing: antialiased;
5 -moz-osx-font-smoothing: grayscale;
6 text-align: center;
7 color: #2c3e50;
8 margin-top: 60px;
9 display: flex;
10 flex-direction: column;
13.dropzone {
14 height: fit-content;
15 min-height: 200px;
16 max-height: 400px;
17 width: 600px;
18 background: #fdfdfd;
19 border-radius: 5px;
20 border: 2px dashed #000;
21 display: flex;
22 flex-direction: column;
23 justify-content: center;
24 align-items: center;
25 margin: 0 auto;
27input[type="file"] {
28 position: absolute;
29 opacity: 0;
30 width: inherit;
31 min-height: 200px;
32 max-height: 400px;
33 cursor: pointer;
36img {
37 width: 50%;
38 height: 50%;
40button {
41 background-color: transparent;
42 border: 2px solid #e74c3c;
43 border-radius: 1em;
44 color: #e74c3c;
45 cursor: pointer;
46 display: flex;
47 align-self: center;
48 font-size: 1rem;
49 margin: 20px;
50 padding: 1.2em 2.4em;
51 text-align: center;
52 text-transform: uppercase;
53 font-family: "Montserrat", sans-serif;
54 font-weight: 700;

You can also use UI libraries like BootstrapVue, Vuetify, etc., to style the app. Here is how your app will look like.

App with styles

How To Upload the Image to Cloudinary

Under scriptmethods create a function named upload that makes a POST request to with the request body being formData containing the file or data URL and the unsigned upload_preset.

1upload: async function () {
2 const res = await fetch(
3 `${this.cloudName}/image/upload`,
4 {
5 method: "POST",
6 body: this.formData,
7 }
8 );
9 const data = await res.json();
10 this.fileName = "";
11 this.preview = null;
12 this.formData = null;
13 this.success = data.public_id;

After sending the POST request, you reset fileName, preview, and formData to their initial values. You also store the public_id of the image in the success variable, which is then displayed to the user.

Add the following code after the button in the template section. This code is displayed after a successful response is returned from the API, and success is not equal to empty strings.

1<h3 v-if="success">File Uploaded Successfully. publicId: {{success}}</h3>

Here is the entire upload operation in action.

Upload Operation GIF

After successful upload, the public_id of the image is displayed on the app.


In this media jam, we saw how to upload images to Cloudinary in Vue.js with drag and drop operation. You uploaded a picture with an unsigned preset in this jam, but you can also upload a picture with a signed preset. For this, you will need to append api_key, timestamp, and signature in the form data along with the file.

Here are some additional resources that can be helpful:

Ashutosh K Singh

JavaScript Developer

I'm a JavaScript Developer & Technical Writer. I develop awesome stuff with JavaScript and love to write about them.