How to upload files using JavaScript
Tags: javascript, webdev, frontend, programming
Published: 2023-08-03 20:07:32

In this guide, we'll learn how to upload files using JavaScript. We'll create our own file uploader with a user-friendly interface and all the must-have features like file validation, uploading progress tracking, drag-and-drop functionality, and even more.

Here's what our result is going to look like:

![Drag-and-drop file uploader](https://ucarecdn.com/73aa138d-ded9-4549-a62e-fcafdbb6ec6a/file-uploader-with-dnd.gif)

No more ado. Let's jump into the coding!

## Accessing files with HTML

The very first step is accessing files to upload. Let's start by asking a user to select local files and then read the content of those files.

First, create an *index.html* with simple [input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file) element with a file type attribute:

```html
<input type="file">
```

Actually, a file input isn’t beneficial. It just allows users to select files from their devices. To send files to a server, we must make an [HTTP request](https://developer.mozilla.org/en-US/docs/Web/HTTP). Well, let’s create a `<form>` element to wrap our `<input>` and add a `<button>` below to submit the form.

Among other things, we need to send a [POST](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) request, which we can do by editing the form’s `method` attribute. To enable more than one file to upload, we should set a [multiple](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/multiple) attribute.

Last but not least is setting a [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) header with an [enctype attribute](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/enctype) to send files contents as a payload.

```html
<form method="post" enctype="multipart/form-data">
  <input name="file" type="file" multiple>
  <button type="submit">Upload</button>
</form>
```

After selecting files and clicking the submit button, you can check your browser's *Network tab* to see the requested data details.

![HTTP request on HTML form submit](https://ucarecdn.com/a6c4d4b2-d7e1-4b04-af8d-76c35b742261/accessing-files-with-HTML.png)

But where does the data go now? By default, the form data is sent to the URL of the page containing the form — the current page, actually. Generally, all the data should be sent to a server to store and be handled. We can set up a server like [Next.js](https://nextjs.org/), but it's a big separate topic to discover. You can learn more about it in [our tutorial](https://uploadcare.com/blog/next-js-tutorial/). In this post, I suggest concentrating on uploading staff and using [httpbin](https://httpbin.org/) — a simple OSS HTTP Request & Response Service.

Let's add the [action attribute](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/action) and try it out!

```html
<form action="https://httpbin.org/post" method="post" enctype="multipart/form-data">
  <input name="file" type="file" multiple>
  <button type="submit">Upload</button>
</form>
```

Now after submitting a form, our page redirects us to an *httpbin* response:

![POST-request response from httpbin](https://ucarecdn.com/82f043ae-5b96-4ebd-9638-ab85018f070a/httpbin-respond.png)

So, the boring part is behind us! Let's move to add a portion of magic with JavaScript ✨

## Uploading files using Fetch API

To submit our form, let's create an *index.js* file, connect the script to our *index.html* file, and set up a `submit` event handler. The first thing we do here is to stop the browser from sending the file to the server by calling the event's method [preventDefault](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault):

```js
const form = document.querySelector('form');
form.addEventListener('submit', handleSubmit);

function handleSubmit(event) {
  event.preventDefault();
}
```

Next, we should construct the HTTP request using the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). The Fetch API expects the first argument to be a URL which we can set here instead of the form’s action property. The second argument is optional and provides an [object](https://developer.mozilla.org/en-US/docs/Web/API/fetch#options) with custom settings to be applied to the request. Using it, we can change the default `GET` method to `POST` and add the request body using [FormData API](https://developer.mozilla.org/en-US/docs/Web/API/FormData).

Let’s make a separate function for file uploading called, you guess it, `uploadFiles`:

```js
function handleSubmit(event) {
  event.preventDefault();

  uploadFiles();
}

function uploadFiles() {
  const url = 'https://httpbin.org/post';
  const formData = new FormData(form);

  const fetchOptions = {
    method: 'post',
    body: formData
  };

  fetch(url, fetchOptions);
}
```

That’s the bare minimum needed to upload files using Fetch API. Check the result through your browser’s *Network tab*:

![Uploading files with Fetch API](https://ucarecdn.com/9730ab0c-0b37-468b-afe3-0aa64449c976/uploading-files-with-fetch.png)

Fetch API is a modern way of fetching resources in JavaScript. It is a more powerful and flexible replacement for [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) in most cases. Unfortunately, Fetch API still doesn't provide any way to track file uploading progress. If we want to implement an ordinary progress bar to improve user experience (and in this article, we will!), we should use the old way of interacting with a server.

With that in mind, let's refactor our solution!

## Uploading files using XMLHttpRequest

Let's rewrite the `uploadFiles` function.

We should create a new request with a [XMLHttpRequest constructor](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/XMLHttpRequest). The request returns an object which we can use for making requests.

First, we call [open() method](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/open) to initialize the request. This method expects two required parameters: `method` and `URL`.

Then, we call [send() method](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/send), where we pass the body of the data. I prefer to send a simple object, but it could be FormData, Blob, and [even more](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/send#parameters).

```js
function uploadFiles() {
  const url = 'https://httpbin.org/post';
  const method = 'post';

  const xhr = new XMLHttpRequest();

  const data = new FormData(form);

  xhr.open(method, url);
  xhr.send(data);
}
```

That’s uploading files with `XMLHttpRequest`. Don’t hesitate to check your *Network tab* to ensure the request also works.

So, our file uploader is already performing its essential function. However, for now, we do not handle errors in any way. That allows us to upload files of any size — poor *httpbin* is hardly designed to accept gigabytes of information from us.

In addition, our file uploader does not look user-friendly at all: we do not track the uploading progress or show information about uploaded files in the interface in any way.

Nobody would want to use such a poor solution. Well, let’s improve it with more features!

## Filtering for a particular file type

In some scenarios, you might want to restrict the types of files that users can upload. For example, you may only want to allow video (e.g., MP4, OGG) or documents (e.g., PDF, DOCX) to be uploaded. To achieve this, you can add a client-side check to filter for a particular file extension even before submitting the form.

Let's transform our file uploader to just an image uploader by adding an [accept attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept) to our input:

```html
<form>
  <input name="file" type="file" multiple accept="image/*">
  <button type="submit">Upload</button>
</form>
```

Often you should limit the user to picking any arbitrary file type. For example, if your file input lets users upload a profile picture, you may want them to select web-compatible image formats, such as JPEG or PNG. Acceptable file types can be specified with the accept attribute, which takes a comma-separated list of allowed file extensions or [MIME types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types):

```html
<form>
  <input name="file" type="file" multiple accept="image/webp, image/jpeg, image/png">
  <button type="submit">Upload</button>
</form>
```

Note that the `accept` attribute doesn't strictly validate the selected files. Instead, it serves as a way to guide users toward choosing the correct file types by providing hints to the browser. It is still possible for users to override this guidance by modifying options in the file chooser. That's why you should ensure the expected requirement is validated server-side.

At the same time, client-side validation is user-friendly. It is a best practice when we validate the file type before submitting it to upload and show related hints to our users. Let's start enhancing our UX by creating a *status* markup below the form:

```html
<p>
  <strong>Uploading status:</strong>
  <span id="statusMessage">🤷‍♂ Nothing's uploaded</span>
</p>
```

Now we need to get references to the status message node, the submit button, and the file input. Let's declare variables for them at the very beginning of our script:

```js
const statusMessage = document.getElementById('statusMessage');
const submitButton = document.querySelector('button');
const fileInput = document.querySelector('input');
```

Having them, we can toggle the submit button and show some messages depending on the uploading state and the list of files the user picked.

To start, let's create a function that will update the status:

```js
function updateStatusMessage(text) {
  statusMessage.textContent = text;
}
```

Then, create a function that will throw an error when the file type is unsupported:

```js
function assertFilesValid(fileList) {
  const allowedTypes = ['image/webp', 'image/jpeg', 'image/png'];

  for (const file of fileList) {
    const { name: fileName } = file;

    if (!allowedTypes.includes(file.type)) {
      throw new Error(`❌ File "${fileName}" could not be uploaded. Only images with the following types are allowed: WEBP, JPEG, PNG.`);
    }
  }
}
```

Now we need to pass a list of files to this function. Let's create an event handler called `handleInputChange`:

```js
function handleInputChange() {
  try {
    assertFilesValid(fileInput.files);
  } catch (err) {
    updateStatusMessage(err.message);
    return;
  }

  submitButton.disabled = false;
}
```

As you see, we get the list of files from the file input and pass it to the `assertFilesValid`. If an error is thrown, we show it to our user. Otherwise, we turn off the submit button to allow the uploading.

The last thing is to set up an `сhange` event handler which is fired each time the user selects files:

```js
fileInput.addEventListener('change', handleInputChange);
```

This is it. Now you can try to upload files in different extensions by yourself to see how it works:

![File type validation](https://ucarecdn.com/e00607f7-bcfd-4b9c-9c5a-ab13c3953e83/file-type-validation.png)

If you pick an unsupported file, the uploader will show an error. However, if then you select the correct file, the uploader won't remove the error. To fix this, create a function called `resetFormState` and call it when the file changes. Also, it's worth disabling the submit button here because we're resetting the form, right?

```js
function resetFormState() {
  submitButton.disabled = true;
  updateStatusMessage(`🤷‍♂ Nothing's uploaded`)
}
```

```js
function handleInputChange(event) {
  resetFormState();

  // the rest of the code goes here
}
```

Cool, it works. But adding more limits can make the uploader even more powerful. Let's continue improving it!

## File size validation

Let's say we only want to allow uploading up to 1MB. We care about our users, so we don't want them to wait while the file uploads on the server only to get an error message. So, we can use JavaScript validation to check that the selected file follows the given requirements.

Let's declare a new variable inside the `assertFilesValid` function called `sizeLimit`, unpack a `fileSize` property from each file, and add one more condition inside the loop:

```js
function assertFilesValid(fileList) {
  const allowedTypes = ['image/webp', 'image/jpeg', 'image/png'];
  const sizeLimit = 1024 * 1024; // 1 megabyte

  for (const file of fileList) {
    const { name: fileName, size: fileSize } = file;

    if (!allowedTypes.includes(file.type)) {
      throw new Error(`❌ File "${fileName}" could not be uploaded. Only images with the following types are allowed: WEBP, JPEG, PNG.`);
    }

    // ↓ the new condition ↓
    if (fileSize > sizeLimit) {
      throw new Error(`❌ File "${fileName}" could not be uploaded. Only images up to 1 MB are allowed.`);
    }
  }
}
```

Check your browser to see it in action:

![File size validation](https://ucarecdn.com/91610030-ed55-4039-8b5d-1c4c3aa7ef9f/file-size-validation.png)

Now let's come back to the uploading. Suppose the user selects a file that satisfies our requirements. In that case, we should build an interface that tracks the uploading process, shows the uploaded file details, and, finally, sends some positive messages.

## Tracking file upload progress

Let's start with a typical progress handler. The [loadend event](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestUpload/loadend_event) is fired on an XHRHttpRequest instance when the request has been completed (doesn't matter whether it is successful or not). Here we enhance the `uploadFiles` function to show the result message to a user:

```js
function uploadFiles() {
  // all the rest code is here

  xhr.addEventListener('loadend', () => {
    if (xhr.status === 200) {
      updateStatusMessage('✅ Success');
    } else {
      updateStatusMessage('❌ Error');
    }
  });
}
```

Hope, while texting that, you'll see a green tick:

![Successful file upload](https://ucarecdn.com/59e44d6f-b925-4f73-a05f-5d0e5f7f853a/successful-upload-message.png)

It's also worth showing a user the loading state and disabling the submit button. Otherwise, the user may not understand what is going on and trigger the uploading multiple times. To do this, create a new function:

```js
function showPendingState() {
  submitButton.disabled = true;
  updateStatusMessage('⏳ Pending...')
}
```

And call prior to uploading:

```js
function handleSubmit(event) {
  event.preventDefault();

  // ↓ here ↓
  showPendingState();

  uploadFiles();
}
```

We can do even better by checking the uploading status dynamically. Let's set up a [progress handler](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestUpload/progress_event) where we could use the [loaded](https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent/loaded) and [total](https://developer.mozilla.org/en-US/docs/Web/API/ProgressEvent/total) properties which indicate the amount of work already performed and total size of the data being processed or transmitted respectively:

```js
function uploadFiles() {
  // all the rest code is here

  xhr.upload.addEventListener('progress', event => {
    updateStatusMessage(`⏳ Uploaded ${event.loaded} bytes of ${event.total}`);
  });
}
```

Note that we're attaching the `progress` event handler not to the `xhr` object itself, but to its `upload` property!

Now you can check the current progress of the upload:

![Pending file upload](https://ucarecdn.com/8f1258c6-7738-4f0d-9c67-eda3683aa230/pending-upload-message.png)

But what feature provides even a more user-friendly experience? Progress bar, obviously!

## File upload progress bar

Actually, we have a [`<progress>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress) to indicate the completion progress of a task. It has two attributes: `max` and `value`. Let's add it to our markup:

```html
<progress value="0" max="100"></progress>
```

The `max` attribute describes how much work the task indicated by the progress element requires. We set its value to 100 to operate it like a percentage. The `value` attribute specifies how much of the task has already been completed. We'll change it progressively via JavaScript.

First, let's declare one more variable globally:

```js
const progressBar = document.querySelector('progress');
```

Then create a function that will update the value of this progress bar:

```js
function updateProgressBar(value) {
  const percent = value * 100;
  progressBar.value = Math.round(percent);
}
```

It gets a fraction value but then transforms it to a percentage, [rounds](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round) to the nearest integer and then sets it as a progress bar value.

Now call it when the `progress` event happens:

```js
xhr.upload.addEventListener('progress', event => {
  updateStatusMessage(`⏳ Uploaded ${event.loaded} bytes of ${event.total}`);
  updateProgressBar(event.loaded / event.total);
});
```

Finally, we reset the value after the uploading completes:

```js
xhr.addEventListener('loadend', () => {
  // the `xhr.status` check is here

  updateProgressBar(0);
});
```

Well, here we are with a simple progress visualization:

![File upload progress bar](https://ucarecdn.com/b7faad0b-e0fb-4d2e-84f0-d67f2a7da771/progress-bar.png)

Now it's time to give our users a little more details about the exact files they have uploaded.

## Getting information about uploaded files

The [FileList](https://developer.mozilla.org/en-US/docs/Web/API/FileList) object collects the information about all the files users select. For instance, that allows us to determine the number of selected files. Let's prepare the markup for our counter:

```html
<p>
  <strong>Uploaded files:</strong>
  <span id="fileNum">0</span>
</p>
```

Then declare a global `fileNum` variable that stores the counter element:

```js
const fileNum = document.getElementById('fileNum');
```

Create a function that will update the counter:

```js
function renderFilesMetadata(fileList) {
  fileNum.textContent = fileList.length;
}
```

Finally, fire the function once the files have been uploaded:

```js
xhr.addEventListener('loadend', () => {
  if (xhr.status === 200) {
    updateStatusMessage('✅ Success');

    // ↓ here ↓
    renderFilesMetadata(fileInput.files);
  } else {
    updateStatusMessage('❌ Error');
  }

  updateProgressBar(0);
});
```

Alright folks, now we talk numbers!

![Count the number of files uploaded](https://ucarecdn.com/52dac108-b8b3-412a-a014-b9dda1e0dd58/uploaded-files-number.png)

Actually, we can extract [more information](https://developer.mozilla.org/en-US/docs/Web/API/File#instance_properties) from the uploaded files. Let's try it out.

First, introduce a list to our markup:

```html
<ul id="fileListMetadata"></ul>
```

Then get access to the list by its ID:

```js
const fileListMetadata = document.getElementById('fileListMetadata');
```

Now we can extract the information from each uploaded file in a loop and render it to the page by using [insertAdjacentHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML) method.

```js
function renderFilesMetadata(fileList) {
  fileNum.textContent = fileList.length;

  fileListMetadata.textContent = '';

  for (const file of fileList) {
    const name = file.name;
    const type = file.type;
    const size = file.size;

    fileListMetadata.insertAdjacentHTML(
      'beforeend',
      `
        <li>
          <p><strong>Name:</strong> ${name}</p>
          <p><strong>Type:</strong> ${type}</p>
          <p><strong>Size:</strong> ${size} bytes</p>
        </li>
      `,
    );
  }
}
```

The last thing is to add these two lines to `resetFormState` function to make sure that metadata resets correctly:

```js
fileListMetadata.textContent = '';
fileNum.textContent = '0';
```

Well, here we are now:

![Getting meta data of a file](https://ucarecdn.com/c2af4613-f141-402d-b69c-94aedb59a7a8/file-meta-information.png

Feel free to try it out in this sandbox:

<CodeSandbox url="https://codesandbox.io/embed/dawn-surf-2mpmkz?fontsize=14&hidenavigation=1&theme=dark" title="Unstyled JavaScript file uploader"/>

I believe we now have all the basic functionality for a simple but powerful file uploader. However, it still looks so... boring. Isn't it? How about adding some styles to make our uploader, you know, awesome? Something like that, huh?

![File uploader with JavaScript](https://ucarecdn.com/ff74749a-932a-49f6-b686-e6d50fe6cad9/final-uploader-look.png)

And you may notice yet another spoiler on the screenshot — our final touch — implementing a drag-and-drop file uploading.

## Drag-and-drop hints

It is well-known that utilizing drag and drop functionality is trivial user interaction with file uploading. It's a more friendly approach than triggering the file selection dialog. Fortunately, modern browsers provide [APIs](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API) that enable us to implement a drag-and-drop file selector and uploader.

Since we're focusing on writing JavaScript logic in this tutorial, I won't dive into customizing our components. You can find the final [markup](https://github.com/tenebricosa/uploading-file-with-js/blob/main/JavaScript%20Uploader/index.html) and [styles](https://github.com/tenebricosa/uploading-file-with-js/blob/main/JavaScript%20Uploader/src/styles.css) in [my repository](https://github.com/tenebricosa/uploading-file-with-js). Next, we will concentrate on the part directly related to implementing drag-and-drop functionality.

First, wrap our form with a new element that would be a drop area:

```html
<div id="dropArea">
  <form>
    <-- the rest of the form is here -->
  </form>
</div>
```

Define a new variable in our script:

```js
const dropArea = document.getElementById('dropArea');
```

The best practice to enhance user experience is to provide clear visual hints whenever a file can be securely dropped. It is often achieved by incorporating a dotted or dashed area. Let's implement the highlighting and start handling the first drag-and-drop events.

In all, there are [eight events](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API#drag_events) the browser fires related to drag and drop, but we'll be going over just four of them: `dragenter`, `dragleave`, `dragover`, and `drop`. Here is a short sum up of their functionality:
* `dragenter` is fired when the dragged item enters over a drop area, making it the target for the drop event.
* `dragleave` is the opposite of `dragenter` and is fired when the dragged item leaves a target drop area.
* `dragover` is fired every few hundred milliseconds while the dragged item is over a target drop area.
* `drop` is fired once the user drops the item onto the target drop area.

As you might expect, you can register event handlers for drag-and-drop events the same way you register event handlers for most browser events.
However, there are a lot of tricky parts related to them.

For instance, you can not intercept `drop` event if you do not prevent default behavior on `dragenter` and `dragover` (read more on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Drag_operations#specifying_drop_targets)). Or it's hard to determine where exactly user is going to drop something because drag-events are fired not only on the drop area itself, but also on its children.

That's why usually the code for drag-n-drop is kind of obscure. To simplify the task, I'm going to reuse [the code](https://github.com/uploadcare/blocks/blob/a37fc34abe7848b046584ab0abc470beb58730d2/blocks/DropArea/addDropzone.js) of Uploadcare engineers who implemented drag-n-drop file uploader properly years ago.

So, let's define a function called `initDropAreaHighlightOnDrag`, which will add `highlight` class to the drop area when the user is dragging the file over it:

```js
function initDropAreaHighlightOnDrag() {
  let dragEventCounter = 0;

  dropArea.addEventListener('dragenter', event => {
    event.preventDefault();

    if (dragEventCounter === 0) {
      dropArea.classList.add('highlight');
    }

    dragEventCounter += 1;
  });

  dropArea.addEventListener('dragover', event => {
    event.preventDefault();

    // in case of non triggered dragenter!
    if (dragEventCounter === 0) {
      dragEventCounter = 1;
    }
  });

  dropArea.addEventListener('dragleave', event => {
    event.preventDefault();

    dragEventCounter -= 1;

    if (dragEventCounter <= 0) {
      dragEventCounter = 0;
      dropArea.classList.remove('highlight');
    }
  });

  dropArea.addEventListener('drop', event => {
    event.preventDefault();

    dragEventCounter = 0;
    dropArea.classList.remove('highlight');
  });
}
```

We're counting the events to deal with the problem of event registration described above.

Do not forget to call this function to make everything work!

## Drag-and-drop file uploader

Okay, now we can finally implement the behaviour of our uploader that happens right after the user drops the item. Actually, we should do all the same things that we did previously while handling form submit.

Define a `drop` event handler and attach it to the drop area:

```js
dropArea.addEventListener('drop', handleDrop);

function handleDrop(event) {
  const fileList = event.dataTransfer.files;

  resetFormState();

  try {
    assertFilesValid(fileList);
  } catch (err) {
    updateStatusMessage(err.message);
    return;
  }

  showPendingState();

  uploadFiles(fileList);
}
```

It looks exactly like `handleSubmit`, but does not work. Since we don't submit our form by using `drop` event, we should get our dropped files list with a `files` property of [DataTransfer](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer) object. Then we should pass this file list to the `assertFilesValid` and `uploadFiles` functions. But the last one does not know how to deal with it. Let's teach it!

Instead of sending the full form, as we did before, now we have to get the passed files and append them to the FormData object:

```js
function uploadFiles(files) {
  // xhr-related code stays the same

  const data = new FormData();

  for (const file of files) {
    data.append('file', file);
  }

  // xhr-related code stays the same
}
```

Yet another minor thing to update here is passing files to the `renderFilesMetadata` function:

```js
renderFilesMetadata(files);
```

Also, do not forget to change the `handleSubmit` handler to work with this new `uploadFiles` function:

```js
function handleSubmit(event) {
  event.preventDefault();

  showPendingState();

  // ↓ pass `fileInput.files` here! ↓
  uploadFiles(fileInput.files);
}
```

That's all you need to know to create a file uploader with JavaScript. See how it changes upon we start with a simple `input` element:

![Drag-and-drop file uploader](https://ucarecdn.com/73aa138d-ded9-4549-a62e-fcafdbb6ec6a/file-uploader-with-dnd.gif)

Now it's a really awesome uploader, isn't it? 🦄

Building a file uploader from scratch is fun but not time-saving. Actually, file uploaders could be much more powerful: control who can upload files by authenticating requests, set up server-side validation, and edit files right before uploading by cropping, rotating, and filtering them.

There is no need to build all those features yourself since the already-baked solution already exists. Let's look at my favorite one — [Uploadcare Blocks](https://github.com/uploadcare/blocks).

## Uploading files with Uploadcare Blocks

`Uploadcare Blocks` is a robust JavaScript library designed to build personalized file-handling services. `Blocks` come with diverse interactive and customizable UI components, elevating users' overall file uploading and processing experience. Additionally, by leveraging Uploadcare's versatile file-handling capabilities, you gain access to features such as [MIME-type filtering](https://uploadcare.com/docs/moderation/#file-types), [signed uploads](https://uploadcare.com/docs/security/secure-uploads/), [image editor](https://uploadcare.com/docs/transformations/image/), and much more.

Getting started with `Blocks` takes just a couple of minutes.

Let's create a new HTML document to connect the script and register `Blocks`:

```html
<script type="module">
  import * as LR from 'https://cdn.jsdelivr.net/npm/@uploadcare/blocks@0.25.1/web/blocks.min.js';

  LR.registerBlocks(LR);
</script>
```

Note that we manually specify the `Blocks` version, so you should replace it with the [latest release](https://github.com/uploadcare/blocks/releases) while implementing.

Start using the File Uploader in your newly created application markup. Uploadcare provides [three solutions](https://uploadcare.com/docs/file-uploader/installation/#choose-a-solution), so you can choose one that best fits your needs. For instance, let's try `inline mode`:

```html
<lr-file-uploader-inline
  css-src="https://cdn.jsdelivr.net/npm/@uploadcare/blocks@0.25.1/web/lr-file-uploader-regular.min.css"
  ctx-name="my-uploader"
>
</lr-file-uploader-inline>
```

The `css-src` attribute is used to specify the URL of the basic CSS styles for the uploader. Don't forget to update the `Blocks` version here as well. The `ctx-name` attribute specifies the name of the uploader context, which allows wire blocks together.

Last but not least is configuring the uploader by adding `<lr-config>`. The `ctx-name` attribute should be similar to the previous one. The `pubkey` attribute should include your public key. Sign up to [Uploadcare](https://app.uploadcare.com/accounts/signup/), get a public API key in Uploadcare project's [dashboard](https://app.uploadcare.com/projects/-/api-keys/), and replace `{YOUR_PUBLIC_KEY}` with your personal one.

```html
<lr-config
  ctx-name="my-uploader"
  pubkey={YOUR_PUBLIC_KEY}
></lr-config>
```

Perfect, now we have a three-minutes-set powerful file uploader right in our application:

![Uploadcare file uploader](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e4vk9nykp9hafzncgczz.png)

Don't trust the screenshots! Discover a [live demo](https://codesandbox.io/s/inspiring-pine-tjwng8?file=/index.html)!

## Awesome wrapping up

Now you’re familiar with all significant concepts of uploading files with JavaScript: multiple file uploads, uploading files using `Fetch API` and `XMLHttpRequest`, filtering for a particular file extension, file size validation, implementing a progress bar, extracting file information, implementing drag-and-drop functionality, and, finally, how to use all that staff and even more using Uploadcare and not wasting time by writing any code from scratch.

Feel free to explore the [source code](https://github.com/tenebricosa/uploading-file-with-js) on GitHub and discover a [live demo](https://codesandbox.io/s/simple-uploader-with-type-filtering-progress-bar-and-dnd-5s9vnh?file=/index.html) implementation.

And you are welcome to learn more about [Uploadcare](https://uploadcare.com/), and discover other tutorials, such us how to upload files with [React](https://uploadcare.com/blog/how-to-upload-file-in-react/) or [Angular](https://uploadcare.com/blog/how-to-upload-files-in-angular/), or considering what kind of magic you can do with the uploaded images following our [extracting colors guide](https://uploadcare.com/blog/color-extraction-solution/) 🎨

That is all for today. Until we meet again, happy reading and stay awesome (just like our uploader) 🦄