<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nastya Kataeva</title>
    <description>The latest articles on DEV Community by Nastya Kataeva (@tenebricosa).</description>
    <link>https://dev.to/tenebricosa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F843685%2F5d009c94-9b76-4e81-96a2-90a78a3c9cc8.jpeg</url>
      <title>DEV Community: Nastya Kataeva</title>
      <link>https://dev.to/tenebricosa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tenebricosa"/>
    <language>en</language>
    <item>
      <title>How to upload files using JavaScript</title>
      <dc:creator>Nastya Kataeva</dc:creator>
      <pubDate>Thu, 03 Aug 2023 20:07:32 +0000</pubDate>
      <link>https://dev.to/uploadcare_org/how-to-upload-files-using-javascript-2759</link>
      <guid>https://dev.to/uploadcare_org/how-to-upload-files-using-javascript-2759</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Here's what our result is going to look like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c_HXPy2---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://ucarecdn.com/73aa138d-ded9-4549-a62e-fcafdbb6ec6a/file-uploader-with-dnd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c_HXPy2---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://ucarecdn.com/73aa138d-ded9-4549-a62e-fcafdbb6ec6a/file-uploader-with-dnd.gif" alt="Drag-and-drop file uploader" width="800" height="768"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No more ado. Let's jump into the coding!&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessing files with HTML
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;First, create an &lt;em&gt;index.html&lt;/em&gt; with simple &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file"&gt;input&lt;/a&gt; element with a file type attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP"&gt;HTTP request&lt;/a&gt;. Well, let’s create a &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; element to wrap our &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; and add a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; below to submit the form.&lt;/p&gt;

&lt;p&gt;Among other things, we need to send a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST"&gt;POST&lt;/a&gt; request, which we can do by editing the form’s &lt;code&gt;method&lt;/code&gt; attribute. To enable more than one file to upload, we should set a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/multiple"&gt;multiple&lt;/a&gt; attribute.&lt;/p&gt;

&lt;p&gt;Last but not least is setting a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type"&gt;Content-Type&lt;/a&gt; header with an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/enctype"&gt;enctype attribute&lt;/a&gt; to send files contents as a payload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt; &lt;span class="na"&gt;enctype=&lt;/span&gt;&lt;span class="s"&gt;"multipart/form-data"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;multiple&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Upload&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After selecting files and clicking the submit button, you can check your browser's &lt;em&gt;Network tab&lt;/em&gt; to see the requested data details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qbieCESK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/a6c4d4b2-d7e1-4b04-af8d-76c35b742261/accessing-files-with-HTML.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qbieCESK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/a6c4d4b2-d7e1-4b04-af8d-76c35b742261/accessing-files-with-HTML.png" alt="HTTP request on HTML form submit" width="800" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt;, but it's a big separate topic to discover. You can learn more about it in &lt;a href="https://uploadcare.com/blog/next-js-tutorial/"&gt;our tutorial&lt;/a&gt;. In this post, I suggest concentrating on uploading staff and using &lt;a href="https://httpbin.org/"&gt;httpbin&lt;/a&gt; — a simple OSS HTTP Request &amp;amp; Response Service.&lt;/p&gt;

&lt;p&gt;Let's add the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/action"&gt;action attribute&lt;/a&gt; and try it out!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"https://httpbin.org/post"&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt; &lt;span class="na"&gt;enctype=&lt;/span&gt;&lt;span class="s"&gt;"multipart/form-data"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;multiple&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Upload&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now after submitting a form, our page redirects us to an &lt;em&gt;httpbin&lt;/em&gt; response:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JBfeI8QY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/82f043ae-5b96-4ebd-9638-ab85018f070a/httpbin-respond.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JBfeI8QY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/82f043ae-5b96-4ebd-9638-ab85018f070a/httpbin-respond.png" alt="POST-request response from httpbin" width="800" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, the boring part is behind us! Let's move to add a portion of magic with JavaScript ✨&lt;/p&gt;

&lt;h2&gt;
  
  
  Uploading files using Fetch API
&lt;/h2&gt;

&lt;p&gt;To submit our form, let's create an &lt;em&gt;index.js&lt;/em&gt; file, connect the script to our &lt;em&gt;index.html&lt;/em&gt; file, and set up a &lt;code&gt;submit&lt;/code&gt; 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 &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault"&gt;preventDefault&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;form&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we should construct the HTTP request using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API"&gt;Fetch API&lt;/a&gt;. 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 &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/fetch#options"&gt;object&lt;/a&gt; with custom settings to be applied to the request. Using it, we can change the default &lt;code&gt;GET&lt;/code&gt; method to &lt;code&gt;POST&lt;/code&gt; and add the request body using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/FormData"&gt;FormData API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s make a separate function for file uploading called, you guess it, &lt;code&gt;uploadFiles&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;uploadFiles&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;uploadFiles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://httpbin.org/post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetchOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s the bare minimum needed to upload files using Fetch API. Check the result through your browser’s &lt;em&gt;Network tab&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MhAsOv0I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/9730ab0c-0b37-468b-afe3-0aa64449c976/uploading-files-with-fetch.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MhAsOv0I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/9730ab0c-0b37-468b-afe3-0aa64449c976/uploading-files-with-fetch.png" alt="Uploading files with Fetch API" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fetch API is a modern way of fetching resources in JavaScript. It is a more powerful and flexible replacement for &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest"&gt;XMLHttpRequest&lt;/a&gt; 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.&lt;/p&gt;

&lt;p&gt;With that in mind, let's refactor our solution!&lt;/p&gt;

&lt;h2&gt;
  
  
  Uploading files using XMLHttpRequest
&lt;/h2&gt;

&lt;p&gt;Let's rewrite the &lt;code&gt;uploadFiles&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;We should create a new request with a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/XMLHttpRequest"&gt;XMLHttpRequest constructor&lt;/a&gt;. The request returns an object which we can use for making requests.&lt;/p&gt;

&lt;p&gt;First, we call &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/open"&gt;open() method&lt;/a&gt; to initialize the request. This method expects two required parameters: &lt;code&gt;method&lt;/code&gt; and &lt;code&gt;URL&lt;/code&gt;.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;uploadFiles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://httpbin.org/post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;post&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;xhr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;XMLHttpRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s uploading files with &lt;code&gt;XMLHttpRequest&lt;/code&gt;. Don’t hesitate to check your &lt;em&gt;Network tab&lt;/em&gt; to ensure the request also works.&lt;/p&gt;

&lt;p&gt;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 &lt;em&gt;httpbin&lt;/em&gt; is hardly designed to accept gigabytes of information from us.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Nobody would want to use such a poor solution. Well, let’s improve it with more features!&lt;/p&gt;

&lt;h2&gt;
  
  
  Filtering for a particular file type
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Let's transform our file uploader to just an image uploader by adding an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept"&gt;accept attribute&lt;/a&gt; to our input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;multiple&lt;/span&gt; &lt;span class="na"&gt;accept=&lt;/span&gt;&lt;span class="s"&gt;"image/*"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Upload&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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 &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types"&gt;MIME types&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;multiple&lt;/span&gt; &lt;span class="na"&gt;accept=&lt;/span&gt;&lt;span class="s"&gt;"image/webp, image/jpeg, image/png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Upload&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the &lt;code&gt;accept&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;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 &lt;em&gt;status&lt;/em&gt; markup below the form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;Uploading status:&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"statusMessage"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;🤷‍♂ Nothing's uploaded&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;statusMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;statusMessage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;submitButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;To start, let's create a function that will update the status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;updateStatusMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;statusMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, create a function that will throw an error when the file type is unsupported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;assertFilesValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allowedTypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/webp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/jpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;fileList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;allowedTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`❌ File "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" could not be uploaded. Only images with the following types are allowed: WEBP, JPEG, PNG.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need to pass a list of files to this function. Let's create an event handler called &lt;code&gt;handleInputChange&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleInputChange&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;assertFilesValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;updateStatusMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;submitButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;The last thing is to set up an &lt;code&gt;сhange&lt;/code&gt; event handler which is fired each time the user selects files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;fileInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleInputChange&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is it. Now you can try to upload files in different extensions by yourself to see how it works:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OYouesT3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/e00607f7-bcfd-4b9c-9c5a-ab13c3953e83/file-type-validation.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OYouesT3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/e00607f7-bcfd-4b9c-9c5a-ab13c3953e83/file-type-validation.png" alt="File type validation" width="800" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;resetFormState&lt;/code&gt; and call it when the file changes. Also, it's worth disabling the submit button here because we're resetting the form, right?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;resetFormState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;submitButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;updateStatusMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`🤷‍♂ Nothing's uploaded`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleInputChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;resetFormState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// the rest of the code goes here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cool, it works. But adding more limits can make the uploader even more powerful. Let's continue improving it!&lt;/p&gt;

&lt;h2&gt;
  
  
  File size validation
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Let's declare a new variable inside the &lt;code&gt;assertFilesValid&lt;/code&gt; function called &lt;code&gt;sizeLimit&lt;/code&gt;, unpack a &lt;code&gt;fileSize&lt;/code&gt; property from each file, and add one more condition inside the loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;assertFilesValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allowedTypes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/webp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/jpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sizeLimit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 1 megabyte&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;fileList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fileSize&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;allowedTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`❌ File "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" could not be uploaded. Only images with the following types are allowed: WEBP, JPEG, PNG.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// ↓ the new condition ↓&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileSize&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sizeLimit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`❌ File "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" could not be uploaded. Only images up to 1 MB are allowed.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check your browser to see it in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Pb0-5tpD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/91610030-ed55-4039-8b5d-1c4c3aa7ef9f/file-size-validation.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Pb0-5tpD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/91610030-ed55-4039-8b5d-1c4c3aa7ef9f/file-size-validation.png" alt="File size validation" width="800" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracking file upload progress
&lt;/h2&gt;

&lt;p&gt;Let's start with a typical progress handler. The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestUpload/loadend_event"&gt;loadend event&lt;/a&gt; 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 &lt;code&gt;uploadFiles&lt;/code&gt; function to show the result message to a user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;uploadFiles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// all the rest code is here&lt;/span&gt;

  &lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loadend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;updateStatusMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;✅ Success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;updateStatusMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;❌ Error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hope, while texting that, you'll see a green tick:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CCtcnSDQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/59e44d6f-b925-4f73-a05f-5d0e5f7f853a/successful-upload-message.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CCtcnSDQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/59e44d6f-b925-4f73-a05f-5d0e5f7f853a/successful-upload-message.png" alt="Successful file upload" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;showPendingState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;submitButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;updateStatusMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⏳ Pending...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And call prior to uploading:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// ↓ here ↓&lt;/span&gt;
  &lt;span class="nx"&gt;showPendingState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;uploadFiles&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;uploadFiles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// all the rest code is here&lt;/span&gt;

  &lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;progress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;updateStatusMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`⏳ Uploaded &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaded&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bytes of &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that we're attaching the &lt;code&gt;progress&lt;/code&gt; event handler not to the &lt;code&gt;xhr&lt;/code&gt; object itself, but to its &lt;code&gt;upload&lt;/code&gt; property!&lt;/p&gt;

&lt;p&gt;Now you can check the current progress of the upload:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UVZCJASO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/8f1258c6-7738-4f0d-9c67-eda3683aa230/pending-upload-message.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UVZCJASO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/8f1258c6-7738-4f0d-9c67-eda3683aa230/pending-upload-message.png" alt="Pending file upload" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But what feature provides even a more user-friendly experience? Progress bar, obviously!&lt;/p&gt;

&lt;h2&gt;
  
  
  File upload progress bar
&lt;/h2&gt;

&lt;p&gt;Actually, we have a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress"&gt;&lt;code&gt;&amp;lt;progress&amp;gt;&lt;/code&gt;&lt;/a&gt; to indicate the completion progress of a task. It has two attributes: &lt;code&gt;max&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt;. Let's add it to our markup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;progress&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt; &lt;span class="na"&gt;max=&lt;/span&gt;&lt;span class="s"&gt;"100"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/progress&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;max&lt;/code&gt; 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 &lt;code&gt;value&lt;/code&gt; attribute specifies how much of the task has already been completed. We'll change it progressively via JavaScript.&lt;/p&gt;

&lt;p&gt;First, let's declare one more variable globally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;progressBar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;progress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create a function that will update the value of this progress bar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;updateProgressBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;percent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;progressBar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;percent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It gets a fraction value but then transforms it to a percentage, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round"&gt;rounds&lt;/a&gt; to the nearest integer and then sets it as a progress bar value.&lt;/p&gt;

&lt;p&gt;Now call it when the &lt;code&gt;progress&lt;/code&gt; event happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;progress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;updateStatusMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`⏳ Uploaded &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaded&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bytes of &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;updateProgressBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaded&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we reset the value after the uploading completes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loadend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// the `xhr.status` check is here&lt;/span&gt;

  &lt;span class="nx"&gt;updateProgressBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, here we are with a simple progress visualization:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--avUuhEj0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/b7faad0b-e0fb-4d2e-84f0-d67f2a7da771/progress-bar.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--avUuhEj0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/b7faad0b-e0fb-4d2e-84f0-d67f2a7da771/progress-bar.png" alt="File upload progress bar" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it's time to give our users a little more details about the exact files they have uploaded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting information about uploaded files
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/FileList"&gt;FileList&lt;/a&gt; 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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;Uploaded files:&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"fileNum"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then declare a global &lt;code&gt;fileNum&lt;/code&gt; variable that stores the counter element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileNum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fileNum&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a function that will update the counter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;renderFilesMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;fileNum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fileList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, fire the function once the files have been uploaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;loadend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xhr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;updateStatusMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;✅ Success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// ↓ here ↓&lt;/span&gt;
    &lt;span class="nx"&gt;renderFilesMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;updateStatusMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;❌ Error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;updateProgressBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright folks, now we talk numbers!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--L18sILHb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/52dac108-b8b3-412a-a014-b9dda1e0dd58/uploaded-files-number.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--L18sILHb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/52dac108-b8b3-412a-a014-b9dda1e0dd58/uploaded-files-number.png" alt="Count the number of files uploaded" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Actually, we can extract &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/File#instance_properties"&gt;more information&lt;/a&gt; from the uploaded files. Let's try it out.&lt;/p&gt;

&lt;p&gt;First, introduce a list to our markup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"fileListMetadata"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then get access to the list by its ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileListMetadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fileListMetadata&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can extract the information from each uploaded file in a loop and render it to the page by using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML"&gt;insertAdjacentHTML&lt;/a&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;renderFilesMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;fileNum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fileList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;fileListMetadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;fileList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;fileListMetadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;insertAdjacentHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;beforeend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;`
        &amp;lt;li&amp;gt;
          &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Name:&amp;lt;/strong&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/p&amp;gt;
          &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Type:&amp;lt;/strong&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/p&amp;gt;
          &amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Size:&amp;lt;/strong&amp;gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bytes&amp;lt;/p&amp;gt;
        &amp;lt;/li&amp;gt;
      `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last thing is to add these two lines to &lt;code&gt;resetFormState&lt;/code&gt; function to make sure that metadata resets correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;fileListMetadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;fileNum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, here we are now:&lt;/p&gt;

&lt;p&gt;![Getting meta data of a file](&lt;a href="https://ucarecdn.com/c2af4613-f141-402d-b69c-94aedb59a7a8/file-meta-information.png"&gt;https://ucarecdn.com/c2af4613-f141-402d-b69c-94aedb59a7a8/file-meta-information.png&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to try it out in this sandbox:&lt;/p&gt;



&lt;p&gt;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?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D5fhULNc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/ff74749a-932a-49f6-b686-e6d50fe6cad9/final-uploader-look.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D5fhULNc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ucarecdn.com/ff74749a-932a-49f6-b686-e6d50fe6cad9/final-uploader-look.png" alt="File uploader with JavaScript" width="800" height="638"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And you may notice yet another spoiler on the screenshot — our final touch — implementing a drag-and-drop file uploading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Drag-and-drop hints
&lt;/h2&gt;

&lt;p&gt;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 &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API"&gt;APIs&lt;/a&gt; that enable us to implement a drag-and-drop file selector and uploader.&lt;/p&gt;

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

&lt;p&gt;First, wrap our form with a new element that would be a drop area:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"dropArea"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;--&lt;/span&gt; &lt;span class="na"&gt;the&lt;/span&gt; &lt;span class="na"&gt;rest&lt;/span&gt; &lt;span class="na"&gt;of&lt;/span&gt; &lt;span class="na"&gt;the&lt;/span&gt; &lt;span class="na"&gt;form&lt;/span&gt; &lt;span class="na"&gt;is&lt;/span&gt; &lt;span class="na"&gt;here&lt;/span&gt; &lt;span class="na"&gt;--&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define a new variable in our script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dropArea&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dropArea&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;In all, there are &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API#drag_events"&gt;eight events&lt;/a&gt; the browser fires related to drag and drop, but we'll be going over just four of them: &lt;code&gt;dragenter&lt;/code&gt;, &lt;code&gt;dragleave&lt;/code&gt;, &lt;code&gt;dragover&lt;/code&gt;, and &lt;code&gt;drop&lt;/code&gt;. Here is a short sum up of their functionality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dragenter&lt;/code&gt; is fired when the dragged item enters over a drop area, making it the target for the drop event.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dragleave&lt;/code&gt; is the opposite of &lt;code&gt;dragenter&lt;/code&gt; and is fired when the dragged item leaves a target drop area.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dragover&lt;/code&gt; is fired every few hundred milliseconds while the dragged item is over a target drop area.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;drop&lt;/code&gt; is fired once the user drops the item onto the target drop area.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;br&gt;
However, there are a lot of tricky parts related to them.&lt;/p&gt;

&lt;p&gt;For instance, you can not intercept &lt;code&gt;drop&lt;/code&gt; event if you do not prevent default behavior on &lt;code&gt;dragenter&lt;/code&gt; and &lt;code&gt;dragover&lt;/code&gt; (read more on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Drag_operations#specifying_drop_targets"&gt;MDN&lt;/a&gt;). 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.&lt;/p&gt;

&lt;p&gt;That's why usually the code for drag-n-drop is kind of obscure. To simplify the task, I'm going to reuse &lt;a href="https://github.com/uploadcare/blocks/blob/a37fc34abe7848b046584ab0abc470beb58730d2/blocks/DropArea/addDropzone.js"&gt;the code&lt;/a&gt; of Uploadcare engineers who implemented drag-n-drop file uploader properly years ago.&lt;/p&gt;

&lt;p&gt;So, let's define a function called &lt;code&gt;initDropAreaHighlightOnDrag&lt;/code&gt;, which will add &lt;code&gt;highlight&lt;/code&gt; class to the drop area when the user is dragging the file over it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;initDropAreaHighlightOnDrag&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;dragEventCounter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;dropArea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dragenter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dragEventCounter&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;dropArea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;highlight&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;dragEventCounter&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;dropArea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dragover&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// in case of non triggered dragenter!&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dragEventCounter&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;dragEventCounter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;dropArea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dragleave&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;dragEventCounter&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dragEventCounter&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;dragEventCounter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;dropArea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;highlight&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;dropArea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;drop&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;dragEventCounter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;dropArea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;highlight&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're counting the events to deal with the problem of event registration described above.&lt;/p&gt;

&lt;p&gt;Do not forget to call this function to make everything work!&lt;/p&gt;

&lt;h2&gt;
  
  
  Drag-and-drop file uploader
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Define a &lt;code&gt;drop&lt;/code&gt; event handler and attach it to the drop area:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;dropArea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;drop&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleDrop&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleDrop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataTransfer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;resetFormState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;assertFilesValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;updateStatusMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;showPendingState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;uploadFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;uploadFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// xhr-related code stays the same&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// xhr-related code stays the same&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yet another minor thing to update here is passing files to the &lt;code&gt;renderFilesMetadata&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;renderFilesMetadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, do not forget to change the &lt;code&gt;handleSubmit&lt;/code&gt; handler to work with this new &lt;code&gt;uploadFiles&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;showPendingState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// ↓ pass `fileInput.files` here! ↓&lt;/span&gt;
  &lt;span class="nx"&gt;uploadFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all you need to know to create a file uploader with JavaScript. See how it changes upon we start with a simple &lt;code&gt;input&lt;/code&gt; element:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c_HXPy2---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://ucarecdn.com/73aa138d-ded9-4549-a62e-fcafdbb6ec6a/file-uploader-with-dnd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c_HXPy2---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://ucarecdn.com/73aa138d-ded9-4549-a62e-fcafdbb6ec6a/file-uploader-with-dnd.gif" alt="Drag-and-drop file uploader" width="800" height="768"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it's a really awesome uploader, isn't it? 🦄&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;There is no need to build all those features yourself since the already-baked solution already exists. Let's look at my favorite one — &lt;a href="https://github.com/uploadcare/blocks"&gt;Uploadcare Blocks&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Uploading files with Uploadcare Blocks
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Uploadcare Blocks&lt;/code&gt; is a robust JavaScript library designed to build personalized file-handling services. &lt;code&gt;Blocks&lt;/code&gt; 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 &lt;a href="https://uploadcare.com/docs/moderation/#file-types"&gt;MIME-type filtering&lt;/a&gt;, &lt;a href="https://uploadcare.com/docs/security/secure-uploads/"&gt;signed uploads&lt;/a&gt;, &lt;a href="https://uploadcare.com/docs/transformations/image/"&gt;image editor&lt;/a&gt;, and much more.&lt;/p&gt;

&lt;p&gt;Getting started with &lt;code&gt;Blocks&lt;/code&gt; takes just a couple of minutes.&lt;/p&gt;

&lt;p&gt;Let's create a new HTML document to connect the script and register &lt;code&gt;Blocks&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;LR&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cdn.jsdelivr.net/npm/@uploadcare/blocks@0.25.1/web/blocks.min.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;LR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;registerBlocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that we manually specify the &lt;code&gt;Blocks&lt;/code&gt; version, so you should replace it with the &lt;a href="https://github.com/uploadcare/blocks/releases"&gt;latest release&lt;/a&gt; while implementing.&lt;/p&gt;

&lt;p&gt;Start using the File Uploader in your newly created application markup. Uploadcare provides &lt;a href="https://uploadcare.com/docs/file-uploader/installation/#choose-a-solution"&gt;three solutions&lt;/a&gt;, so you can choose one that best fits your needs. For instance, let's try &lt;code&gt;inline mode&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;lr-file-uploader-inline&lt;/span&gt;
  &lt;span class="na"&gt;css-src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/@uploadcare/blocks@0.25.1/web/lr-file-uploader-regular.min.css"&lt;/span&gt;
  &lt;span class="na"&gt;ctx-name=&lt;/span&gt;&lt;span class="s"&gt;"my-uploader"&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/lr-file-uploader-inline&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Last but not least is configuring the uploader by adding &lt;code&gt;&amp;lt;lr-config&amp;gt;&lt;/code&gt;. The &lt;code&gt;ctx-name&lt;/code&gt; attribute should be similar to the previous one. The &lt;code&gt;pubkey&lt;/code&gt; attribute should include your public key. Sign up to &lt;a href="https://app.uploadcare.com/accounts/signup/"&gt;Uploadcare&lt;/a&gt;, get a public API key in Uploadcare project's &lt;a href="https://app.uploadcare.com/projects/-/api-keys/"&gt;dashboard&lt;/a&gt;, and replace &lt;code&gt;{YOUR_PUBLIC_KEY}&lt;/code&gt; with your personal one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;lr-config&lt;/span&gt;
  &lt;span class="na"&gt;ctx-name=&lt;/span&gt;&lt;span class="s"&gt;"my-uploader"&lt;/span&gt;
  &lt;span class="na"&gt;pubkey=&lt;/span&gt;&lt;span class="s"&gt;{YOUR_PUBLIC_KEY}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;gt;&amp;lt;/lr-config&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perfect, now we have a three-minutes-set powerful file uploader right in our application:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g_1g4fii--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e4vk9nykp9hafzncgczz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g_1g4fii--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e4vk9nykp9hafzncgczz.png" alt="Uploadcare file uploader" width="800" height="750"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Don't trust the screenshots! Discover a &lt;a href="https://codesandbox.io/s/inspiring-pine-tjwng8?file=/index.html"&gt;live demo&lt;/a&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  Awesome wrapping up
&lt;/h2&gt;

&lt;p&gt;Now you’re familiar with all significant concepts of uploading files with JavaScript: multiple file uploads, uploading files using &lt;code&gt;Fetch API&lt;/code&gt; and &lt;code&gt;XMLHttpRequest&lt;/code&gt;, 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.&lt;/p&gt;

&lt;p&gt;Feel free to explore the &lt;a href="https://github.com/tenebricosa/uploading-file-with-js"&gt;source code&lt;/a&gt; on GitHub and discover a &lt;a href="https://codesandbox.io/s/simple-uploader-with-type-filtering-progress-bar-and-dnd-5s9vnh?file=/index.html"&gt;live demo&lt;/a&gt; implementation.&lt;/p&gt;

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

&lt;p&gt;That is all for today. Until we meet again, happy reading and stay awesome (just like our uploader) 🦄&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>programming</category>
    </item>
    <item>
      <title>Browserslist: building modern web apps for diverse global audience</title>
      <dc:creator>Nastya Kataeva</dc:creator>
      <pubDate>Wed, 24 Aug 2022 16:25:00 +0000</pubDate>
      <link>https://dev.to/cubejs/browserslist-building-modern-web-apps-for-diverse-global-audience-5g9h</link>
      <guid>https://dev.to/cubejs/browserslist-building-modern-web-apps-for-diverse-global-audience-5g9h</guid>
      <description>&lt;p&gt;Nowadays, web applications are usually built for diverse audience that spans worldwide across countries and continents. Upon checking web analytics, you would probably find that your audience is almost evenly divided between Americas, Asia, and Europe—and you surely can’t ignore Africa and Oceania:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3ree936dxgbl8qt4490.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3ree936dxgbl8qt4490.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Web browser usage differs substantially across regions. For instance, iOS devices are far less popular in Netherlands than in the US. In China, you will see popular local browsers like &lt;a href="https://en.wikipedia.org/wiki/Tencent_QQ" rel="noopener noreferrer"&gt;QQ Browser&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/UC_Browser" rel="noopener noreferrer"&gt;UC Browser&lt;/a&gt; that aren’t present anywhere else in the world:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fim2.ezgif.com%2Ftmp%2Fezgif-2-58519c499f.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fim2.ezgif.com%2Ftmp%2Fezgif-2-58519c499f.webp" alt="Browser usage in the US, Netherlands, and China"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, support for modern web technology also differs across browsers and browser versions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fofpbry9dt40kftwnod5c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fofpbry9dt40kftwnod5c.png" alt="WebP image format supporting"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, we as web developers need to find the right balance between the user experience (UX) of the applications we’re building and our own developer experience (DX):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we’d like our apps to work flawlessly in any browser that our audience uses&lt;/li&gt;
&lt;li&gt;we’d also like our apps to be smaller in size so they download faster&lt;/li&gt;
&lt;li&gt;at the same time, we’d like to use modern JavaScript and CSS features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, we have great tooling for that: &lt;a href="https://github.com/postcss/autoprefixer" rel="noopener noreferrer"&gt;Autoprefixer&lt;/a&gt;, &lt;a href="https://github.com/postcss/postcss" rel="noopener noreferrer"&gt;PostCSS&lt;/a&gt; and &lt;a href="https://github.com/ismay/stylelint-no-unsupported-browser-features" rel="noopener noreferrer"&gt;Stylelint&lt;/a&gt; for CSS transformation, &lt;a href="https://babeljs.io/" rel="noopener noreferrer"&gt;Babel&lt;/a&gt; and &lt;a href="https://webpack.js.org" rel="noopener noreferrer"&gt;Webpack&lt;/a&gt; for JavaScript transpilation and bundling, &lt;a href="https://eslint.org" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt; for code analysis, and many others.&lt;/p&gt;

&lt;p&gt;But how do we tell all these tools about the audience of our application, browsers, and supported features?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/browserslist/browserslist" rel="noopener noreferrer"&gt;Browserslist&lt;/a&gt; to the rescue!&lt;/strong&gt; Browserslist is a library that helps share the browser compatibility configuration with front-end tools. All popular tools, including the ones listed above, already work with Browserslist. More tools integrate as we speak: for instance, &lt;a href="https://github.com/vercel/next.js" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; joined the club in the &lt;a href="https://nextjs.org/blog/next-12-2#other-improvements" rel="noopener noreferrer"&gt;v12.2 release&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Browserslist guarantees that your web application would work in all target browsers; JavaScript and CSS code would be correctly transpiled. On top of that, you’ll get smaller bundle size and application load time.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to get started with Browserslist
&lt;/h2&gt;

&lt;p&gt;First, you need to add a Browserslist config right to the &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;browserslist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;0.1%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This config matches all browser versions that are used by at least 0.1 % of the audience worldwide. &lt;a href="https://www.npmjs.com/package/autoprefixer" rel="noopener noreferrer"&gt;Autoprefixer&lt;/a&gt;, &lt;a href="https://babeljs.io/" rel="noopener noreferrer"&gt;Babel&lt;/a&gt;, and &lt;a href="https://github.com/browserslist/browserslist#browserslist-" rel="noopener noreferrer"&gt;other tools&lt;/a&gt; will find target browsers automatically and bundle the code accordingly.&lt;/p&gt;

&lt;p&gt;But what exactly are these target browsers and their versions? Browserslist provides a CLI interface to check that:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="nx"&gt;npx&lt;/span&gt; &lt;span class="nx"&gt;browserslist&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;0.1%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Voila! We get browsers ranging from QQ Browser and UC Browser to Internet Explorer and Opera Mini. They are still not going anywhere:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvbyfwvi8lb8obyp9d6g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvbyfwvi8lb8obyp9d6g.png" alt="All browser versions that are used by at least 0.1 % of the audience worldwide"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Actually, the queries may be much more complicated than that: let’s include the most recent versions of all browsers that have been just released, exclude dead browsers, and leave only those ones that support &lt;a href="https://caniuse.com/es6-module" rel="noopener noreferrer"&gt;ES6 modules&lt;/a&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="nx"&gt;npx&lt;/span&gt; &lt;span class="nx"&gt;browserslist&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;0.1% and last 2 versions and not dead and supports es6-module&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zgqy0ks9pnymb66n8n8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6zgqy0ks9pnymb66n8n8.png" alt="The most recent versions of all browsers, exclude dead browsers, and leave only those ones that support ES6 modules"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Browserslist’s CLI interface is good enough to quickly check a single query. However, when composing and experimenting with a query for your production application, this can be helpful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;better data visualization for audience coverage&lt;/li&gt;
&lt;li&gt;query linter and query syntax docs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Goodbye, console. Meet &lt;a href="https://browsersl.ist/" rel="noopener noreferrer"&gt;browsersl.ist&lt;/a&gt; website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Easy way to check compatible browsers
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://browsersl.ist/" rel="noopener noreferrer"&gt;browsersl.ist&lt;/a&gt; website is the companion tool for Browserslist. Check it out!&lt;/p&gt;

&lt;p&gt;First, it can help with the audience coverage. It’s recommended to use the &lt;code&gt;defaults&lt;/code&gt; query as the starting point because this query provides reasonable configuration for most users. It covers global audience and matches recent versions of popular and well-maintained browsers worldwide:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flsfqnurjkxbh4yan6ed5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flsfqnurjkxbh4yan6ed5.png" alt="Browsers matches recent versions of popular and well-maintained browsers worldwide"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can view the coverage for particular continents and countries. Apparently, seals and penguins as well as other inhabitants of the research stations in Antarctica &lt;a href="https://browsersl.ist/?q=%3E0%25+in+alt-an&amp;amp;region=alt-an" rel="noopener noreferrer"&gt;prefer to use Safari&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbv36ldvtkrzljs0v1eog.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbv36ldvtkrzljs0v1eog.png" alt="Browser versions used in Antarctica"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The coverage data is shared with the &lt;a href="https://caniuse.com" rel="noopener noreferrer"&gt;Can I Use&lt;/a&gt; database, so you can check which browser versions support certain features such as &lt;a href="https://caniuse.com/css-container-queries" rel="noopener noreferrer"&gt;CSS Container Queries&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F80vclxwa7lah6letaijc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F80vclxwa7lah6letaijc.png" alt="Browsers that supports CSS Container Queries"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Browserslist has a rich query syntax to fine-tune the query to your audience. So, &lt;a href="https://browsersl.ist/" rel="noopener noreferrer"&gt;browsersl.ist&lt;/a&gt; website provides complete documentation on query features; you might be surprised to find there that you can also target Node.js versions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrv01kcbkz0u0099du9e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzrv01kcbkz0u0099du9e.png" alt="Node.js documentation on queries"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next up: try Browserslist and &lt;a href="https://browsersl.ist/" rel="noopener noreferrer"&gt;browsersl.ist&lt;/a&gt; today
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/browserslist/browserslist" rel="noopener noreferrer"&gt;Browserslist&lt;/a&gt; is a must-have tool for building modern web applications.&lt;/p&gt;

&lt;p&gt;Here at &lt;a href="https://cube.dev/?utm_source=dev-to&amp;amp;utm_medium=post&amp;amp;utm_campaign=browserslist" rel="noopener noreferrer"&gt;Cube&lt;/a&gt;, where we build the API for modern data apps, we use it across all our websites, including &lt;a href="https://cube.dev/blog/" rel="noopener noreferrer"&gt;blog&lt;/a&gt;, &lt;a href="https://cube.dev/docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt;, and data application &lt;a href="https://highcharts-demo.cube.dev/#/" rel="noopener noreferrer"&gt;examples&lt;/a&gt;. With Browserslist, we’re sure that Cube’s global developer community gets the best experience possible. We’re also okay to serve ~10 % less JavaScript and CSS code because of smaller bundles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now, it's time to visit &lt;a href="https://browsersl.ist/" rel="noopener noreferrer"&gt;browsersl.ist&lt;/a&gt; and see it in action!&lt;/strong&gt; Whether you’re new to Browserslist or not, &lt;a href="http://browsersl.ist/" rel="noopener noreferrer"&gt;browsersl.ist&lt;/a&gt; is the best playground.&lt;/p&gt;

&lt;p&gt;Don't hesitate to share a link to &lt;a href="http://browsersl.ist/" rel="noopener noreferrer"&gt;browsersl.ist&lt;/a&gt; with your friends or post queries with surprising or unexpected results on social media. We'd love to know what you think, so please leave your feedback in the comments and &lt;a href="https://twitter.com/Browserslist" rel="noopener noreferrer"&gt;tag us on Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
