File uploads with image previews and remove link using Array and readAsDataURL I got my code working with vanilla JavaScript, however I'm new to JS, so I don't know if the code is robust or not. Any advice to improve the code is welcome.
Note: I just noticed a bug. The code removes the image from the DOM (is it the right term?) but it still submits the images that I deleted. Any idea how can I fix that?


 let count = 0

 function previewFiles(input) {
   const preview = document.getElementById('preview')
   const {
     files
   } = input

   Array.from(files).forEach(file =&gt; {
     const reader = new FileReader()

     reader.onload = e =&gt; {
       const div = document.createElement('div')
       const img = document.createElement('img')
       const button = document.createElement('button')

       img.src = e.target.result
       img.width = 200
       img.height = 200
       img.alt = `file-${++count}`

       button.textContent = "Delete"
       button.addEventListener('click', () =&gt; {
         preview.removeChild(div)
       })

       div.appendChild(img)
       div.appendChild(button)

       preview.appendChild(div)
     }

     reader.readAsDataURL(file)
   })
 }
    &lt;input type="file" multiple onchange="previewFiles(this);" /&gt;
    &lt;div id="preview"&gt;&lt;/div&gt;
Welcome to Code Review! Good on you for opening up to constructive criticism.
Buckle up, this will be a long one ;)

 The Question
Let us start with your question:

[...] I don't know if the code is robust or not. Any advice to improve the code is welcome.

I hope you would agree with me when I say that this is not really a proper question. Code &quot;robustness&quot; is a rather subjective matter, so one couldn't objectively answer that. If you would like my opinion: any code that does what it needs to do, everytime you run it, is robust.
I could direct you to the guide on posting a proper question, but I find that is often unnecessary, since I understand what you would want from me so you can improve this code.
Another problem with your question is that you have a bug in your code. Fixing bugs is not really something we do at Code Review, but the way I see it, we can both improve your code and fix the bug in one swoop!
I'm going to review your code nonetheless, because it's perfectly possible, it's fun and people who come here to get their code reviewed can get easily discouraged with all these rules.
The way I measure code improvement in my day-to-day reviews, is by how much smaller you can make a file or function, while maintaining full functionality and  maintaining or improving readability. I'm not really interested in code speed, so you'd have to look elsewhere for tips on that. Let's get to work.

 The problem
The (in my opinion) most valuable advice one could give to a beginning programmer is the following:
Before you start on any given programming problem, you must first understand the problem, and the sub-problems that make up the larger problem, ad infinitum.
In your case it would require you to state and disect the problem as follows:

Create a File upload with image previews and removal functionality

How does one upload a file?

What does it mean to &quot;upload&quot; a file?


How does one remove an uploaded file?

What does it mean to &quot;remove&quot; an uploaded file?


How does one show a thumbnail for an uploaded file?

What is a thumbnail?
How do you get a thumbnail from an uploaded file?
How do you link the remove functionality to a thumbnail?





Thence we can figure out how to write code that solves these sub-problems, and combine those solutions into one big solution.

 How does one upload a file?
This is a part of the problem you've understood. To upload a file with HTML, you use the input tag with type=file. The interesting part here is the next step: what does it mean to &quot;upload&quot; a file?
According to the html specification for &lt;input type=&quot;file&quot;/&gt;, an input tag keeps track of what items have been uploaded, known as &quot;the selected files&quot;, under a property called files. So for a file to &quot;be uploaded&quot;, it must be in the files property of the input. It would seem your mental model does not match the actual implementation of the input tag! That's where the bug comes from.
When you upload a file in your snippet, it overwrites the last selection of uploaded files. You probably thought you fixed it using the multiple attribute, but that attribute only specifically allows multiple files to be uploaded at once. This means that everytime you select any file(s), you lose reference to the previously uploaded files.
Now that we know what we want and understand the problems, we can define a bit of code to do what we want.
&lt;script&gt;
const files = [];

function upload (input) {
  files.push(...input.files);
}
&lt;/script&gt;
&lt;input type=&quot;file&quot; multiple onchange=&quot;upload(this)&quot;/&gt;

There we go. That's all we needed: an array to store our uploaded files and a function to add to that array any file we upload.
A couple of quick notes about this solution:

input.files is a FileList, not an array -- you knew and understood this. One could transform it to an array and use array methods on it, but I chose to use the ... spread operator. In this context, to spread a FileList means to take every File in the FileList, and provide it as an argument to files.push(...). Other solutions like for (const file of input.files) would also work, with the benefit that these methods don't create an array which we never directly use for anything but looping over.


❌ How does one &quot;remove&quot; an uploaded file?
The way you remove a file in your current code is not actually removing the file. You are just removing the thumbnail from the DOM (that is indeed the right term).
But then what does it mean to &quot;remove&quot; an uploaded file? Well, simply put: if &quot;uploading&quot; means that we add a File to an array, &quot;removing&quot; should just mean to take that File out of the array again:
&lt;script&gt;
const files = [];

function upload (input) {
  files.push(...input.files);
}

function remove (name) {
  const index = files.findIndex(file =&gt; file.name === name);
  if (index === -1) return;
  files.splice(index, 1);
}
&lt;/script&gt;
&lt;input type=&quot;file&quot; multiple onchange=&quot;upload(this)&quot;/&gt;

A couple of quick notes about this solution:

To remove an element from an array in JavaScript you need to use Array.splice, which requires an index from where to start removing elements, and the number of items to delete from there. files.splice(index, 1) means to remove 1 item at index index.
To know which file we want to delete from the list, we need a way to differentiate between files. The File API defines a property &quot;name&quot; which we can use.
To know the index of the file we differentiated, we need to use Array.findIndex, which takes a function as a parameter and returns either the index of that file, or -1 (meaning the file was not found in the array).
In case we try to remove a non-existing file, we return. This should in theory never happen, but you should always cover your non-happy flows. That's just good software etiquette.


️ How does one show a thumbnail for an uploaded file?
This is not a simple question. To show a thumbnail, we must first understand what a thumbnail is.
 What is a thumbnail?
Simply put, if a given file is an image, we want to show a small representation of that image. There's already a problem: your input accepts files of any type. If you upload a pdf or an illustrator file, your code will not work. Again, if you think about the problem and define what you do and do not want, you can create a solution.
The simplest approach is to allow any image, and nothing else. One could make custom rendering logic for all sorts of files; a fun exercise for the reader ;).
HTML has got us covered: you can specify what type of file your input will accept. You can read the entire page (which is quite interesting so why not), but the main takeaway here is that you can use the accept attribute with a value of &quot;image/*&quot; to accept only files that are images:
&lt;script&gt;
  const files = [];

  function upload (input) {
    files.push(...input.files);
  }

  function remove (name) {
    const index = files.findIndex(file =&gt; file.name === name);
    if (index === -1) return;
    files.splice(index, 1);
  }
&lt;/script&gt;
&lt;input type=&quot;file&quot; multiple accept=&quot;image/*&quot; onchange=&quot;upload(this)&quot;/&gt;

A couple of quick notes about this solution:

An added benefit is that the HTML specification allows user agents to use this constraint to display only images in the file picker dialog.
As mentioned in the spec, there is still no guarantee that uploaded files will really, really be images. We will keep this in mind for the last step.

➡️️ How do you get a thumbnail from an uploaded file?
Now that we allow only images, we need a way to display uploaded files as thumbnails. You did a good job discovering that you can use the FileReader API to transform a File to an img in the DOM. The method itself is solid, but the approach could be a bit more tactical. This will be the largest code chunk of this answer, so I'm going to annotate it using comments.
&lt;script&gt;
  const files = [];

  function upload (input) {
    files.push(...input.files);
    updateThumbnails();
  }

  function remove (name) {
    const index = files.findIndex(file =&gt; file.name === name);
    if (index === -1) return;
    files.splice(index, 1);
    updateThumbnails();
  }

  function makeThumbnail (file) {
    // We need to use all sorts of asynchronous methods to read files and create images
    // so we are returning a new Promise, which allows us to specify when the loading is done
    // and in turn run code only after we say it's done.
    // If you don't know about promises, you really should read up on them!
    return new Promise((resolve) =&gt; {
      const reader = new FileReader();

      function onLoad (event) {
        // We create a function that we will run when our FileReader successfully
        // reads a file
        const img = new Image(200, 200); // we can use the Image API to directly specify size and skipping the whole &quot;document.createElement&quot; bit
        img.alt = file.name; // We can use the filename as an alt. We don't need a counter

        img.onload = () =&gt; resolve(img); // If an image could be constructed from the data, we resolve our promise with that image
        img.onerror = () =&gt; resolve(null); // We need a representation for a failed image. You could reject the promise, but that would require catching errors and all that yucky stuff. We can just resolve the special value &quot;null&quot; so we can handle it later on

        img.src = event.target.result; // start loading our dataUrl
      }

      reader.onload = onLoad; // If the reader sucessfully made a dataUrl, trigger the onLoad function we defined.
      reader.onerror = () =&gt; resolve(null); // If the reader couldn't create a dataUrl, there's no point in trying to make an image from it, so we resolve with &quot;null&quot; to signify a failed load

      reader.readAsDataURL(file); // Start the entire loading logic
    });
  }

  // This is a little helper function that removes all children of an element.
  function removeChildren (node) {
    while (node.lastChild)
      node.lastChild.remove();
    return node;
  }

  function updateThumbnails () {
    const thumbnails = document.getElementById(&quot;thumbnails&quot;);
    Promise
      .all(files.map(makeThumbnail)) // Promise.all(promises) creates a promise that resolve when all &quot;promises&quot; have been resolved. This will be the array of our images or nulls.
      .then(images =&gt; images.filter(img =&gt; img !== null)) // For our case we just want to ignore failed thumbnails, but you can do smarter things here.
      .then(images =&gt; removeChildren(thumbnails).append(...images)); // When we have new thumbnails, remove the old thumbnails and add the new ones.
  }
&lt;/script&gt;
&lt;input type=&quot;file&quot; multiple accept=&quot;image/*&quot; onchange=&quot;upload(this)&quot;/&gt;
&lt;div id=&quot;thumbnails&quot;&gt;&lt;/div&gt;


It works!
A couple of quick notes about this solution:

Like I said in the first comment, you're going to need a solid understanding of Promises. If I were to go into detail here this answer would become far too large.
A thumbnail typically is a version of an image with a much smaller resolution to save data. This solution just loads the entire image into memory, which could be troublesome in case of many uploaded files.

️❌ How do you link the remove functionality to a thumbnail?
Now that we have thumbnails and know what it means to remove a file, we can link these together. This is now an easy step.
&lt;script&gt;
  const files = [];

  function upload (input) {
    files.push(...input.files);
    updateThumbnails();
  }

  function remove (name) {
    const index = files.findIndex(file =&gt; file.name === name);
    if (index === -1) return;
    files.splice(index, 1);
    updateThumbnails();
  }

  function makeThumbnail (file) {
    return new Promise((resolve) =&gt; {
      const reader = new FileReader();

      function onLoad (event) {
        const img = new Image(200, 200);
        img.alt = file.name;

        img.onload = () =&gt; {
          // instead of directly resolving with img, we create a container and
          // resolve our promise with the container instead
          const container = document.createElement(&quot;div&quot;);
          container.classList.add(&quot;thumbnail&quot;); // add a class so you can style it using css or something fun

          const button = document.createElement(&quot;button&quot;);

          // This is the interesting part! We have access to the &quot;remove&quot; function here,
          // as well as the &quot;file&quot; and its name. We can use those to remove the file when
          // we click the button, which in turn triggers a re-render of the thumbnails.
          button.addEventListener(&quot;click&quot;, () =&gt; remove(file.name));

          container.append(img, button);
          resolve(container);
        };

        img.onerror = () =&gt; resolve(null);

        img.src = event.target.result;
      }

      reader.onload = onLoad;
      reader.onerror = () =&gt; resolve(null);

      reader.readAsDataURL(file);
    });
  }

  function removeChildren (node) {
    while (node.lastChild)
      node.lastChild.remove();
    return node;
  }

  function updateThumbnails () {
    const thumbnails = document.getElementById(&quot;thumbnails&quot;);
    // A little extra code to reset the input after every load.
    // This way you can upload the same files again.
    const input = document.getElementById(&quot;files&quot;);
    input.value = &quot;&quot;;
    Promise
      .all(files.map(makeThumbnail))
      .then(images =&gt; images.filter(img =&gt; img !== null))
      .then(images =&gt; removeChildren(thumbnails).append(...images));
  }
&lt;/script&gt;
&lt;input id=&quot;files&quot; type=&quot;file&quot; multiple accept=&quot;image/*&quot; onchange=&quot;upload(this)&quot;/&gt;
&lt;div id=&quot;thumbnails&quot;&gt;&lt;/div&gt;

A couple of quick notes about this solution:

Since some files may share the same name, upon removing an image, it will remove the first image from the array with that name. We can't do much about that using the native API, but you could assign some id to every file upon &quot;uploading&quot; them. That shouldn't be too hard.
All this work really only gives us a DOM representation of our input, but if you would like to upload these files using a &lt;form&gt; you're out of luck. You would need to define custom logic in order to upload the const files array upon form submission -- but since we're already heavy into JavaScript-land here, you might just want to do that. In that case, have a look at the Fetch API which allows you to post blob data (Files implement the Blob interface).


 Conclusion
We've done it -- through blood, sweat and tears perhaps.
Hopefully you learned a thing or two, otherwise this was probably really boring... :)
So how did we do according to my metric of

how much smaller you can make a file or function, while maintaining full functionality and maintaining or improving readability

Well we increased the size of the code... We went from 34 lines to 50. However, there's an important reason: we only care for less code while maintaining full functionality and maintaining or improving readability. We increased functionality, and also made sure the code is actually always correct in a happy flow.
To link that to your inquiry about &quot;robustness&quot;: we defined what it means for your code to work, and we made sure it works in every case we want it to. That's robust code in my opinion.
I would also argue that, since we've neatly packed our functionality into reasonably small functions, it's crystal clear what each part of the code does. Your snippet was clear to me as well, so it's not an improvement per se, but that's quite the achievement when you change a large part of the code.
The main takeaways are:

Define the problem, then define the problem, then define the problem...
Think about what the code should do, but also about what it shouldn't do (and make sure it doesn't).
Whenever asynchronous code will run, such as events, you could probably use a Promise to streamline the interface.
I would personally like it better if you would use only one way of defining functions. There are subtle differences in implementation between function and () =&gt; {} but you're not leveraging those differences. You could just use either notation to be consistent and reduce the amount of &quot;WTF?!&quot;'s coming from the review room.


You're already quite proficient with JavaScript; you're using fancy syntax such as const and () =&gt; body, you have researched and understood intricate concepts of the browser environment and you're using as much vanilla JS and HTML as possible. Very nice!
Here's the final snippet:


&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
  &lt;meta charset="UTF-8"&gt;
  &lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&gt;
  &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
  &lt;title&gt;Document&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;script&gt;
    const files = [];

    function upload (input) {
      files.push(...input.files);
      updateThumbnails();
    }

    function remove (name) {
      const index = files.findIndex(file =&gt; file.name === name);
      if (index === -1) return;
      files.splice(index, 1);
      updateThumbnails();
    }

    function makeThumbnail (file) {
      return new Promise((resolve) =&gt; {
        const reader = new FileReader();

        function onLoad (event) {
          const img = new Image(200, 200);
          img.alt = file.name;

          img.onload = () =&gt; {
            const container = document.createElement("div");
            container.classList.add("thumbnail");

            const button = document.createElement("button");
            button.textContent = "❌";
            button.addEventListener("click", () =&gt; remove(file.name));

            container.append(img, button);
            resolve(container);
          };

          img.onerror = () =&gt; resolve(null);

          img.src = event.target.result;
        }

        reader.onload = onLoad;
        reader.onerror = () =&gt; resolve(null);

        reader.readAsDataURL(file);
      });
    }

    function removeChildren (node) {
      while (node.lastChild)
        node.lastChild.remove();
      return node;
    }

    function updateThumbnails () {
      const thumbnails = document.getElementById("thumbnails");
      const input = document.getElementById("files");
      input.value = "";
      Promise
        .all(files.map(makeThumbnail))
        .then(images =&gt; images.filter(img =&gt; img !== null))
        .then(images =&gt; removeChildren(thumbnails).append(...images));
    }
  &lt;/script&gt;
  &lt;input type="file" id="files" multiple accept="image/*" onchange="upload(this)"/&gt;
  &lt;div id="thumbnails"&gt;&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;