Part of building a great user experience for users is allowing them to see the impact of their actions. When it comes to uploading images, one technique you can use to improve the UX of your applications is to preview the file before committing to the expensive operation of transmitting the file across the internet.

In this short post, we’ll look at how you can use a few lines of JavaScript to process the contents of a file input on the client. The result of the processing will be used to update an existing img tag to display the image.

The File Input and The Image

You’ll need to start with an HTML input element of type file. In this case, we also want to use some input filtering to make sure that users can only select image files. We can use the accept attribute with a value of image/* to filter possible choices in the user’s native file picker.

<input type="file" name="image" class="form-control" accept="image/*" />

Great! Now that you have the file input, you’ll need a target img tag, which will render the preview image. I’m using a placeholder image to make sure the initial page load renders something to our users.

<img id="image-preview" 
     src="https://via.placeholder.com/400"
     style="width:400px"
     class="rounded rounded-circle" alt="placeholder">

With a bit of Bootstrap styling, you can have a UI that looks something like the following.

client-side preview of HTML so far

Now, let’s move on to the JavaScript.

FileReader and JavaScript Solution

If you’re familiar with HTML at all, you’ll know that many HTML elements have events that you can subscribe to. In the case of this post, we want to use the onchange event of the file input. You’ll also want to pass in a reference to the input element itself, and the target img element. Let’s take a look at the input element after wiring up the onchange event.

<input type="file" name="image" class="form-control" accept="image/*"
       onchange="updatePreview(this, 'image-preview')" >

You’ll notice a updatePreview JavaScript method in the onchange attribute of our input element. The parameters for the method include the this keyword, which applies to the element itself, and the identifier of our image, image-preview. Let’s take a look at the implementation of the updatePreview function.

function updatePreview(input, target) {
    let file = input.files[0];
    let reader = new FileReader();

    reader.readAsDataURL(file);
    reader.onload = function () {
        let img = document.getElementById(target);
        // can also use "this.result"
        img.src = reader.result;
    }
}

The most important item in the JavaScript function is the FieldReader type, which allows you to convert the bytes of the input element into a data url. You can use the data url as the value of src for our target img element.

When you wire up all the elements, you get a responsive client-side user preview.

final client-side preview running in browser

The FileReader type can be used to read all kinds of files and extract information before sending the file to the server. You can read text files, images, video files, and more.

For folks interested in seeing the complete HTML for the solution, here it is.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css">
</head>
<body>
<section class="py-5 text-center container">
    <div class="row py-lg-5 bg-light">
        <div class="col-lg-6 col-md-8 mx-auto">
            <h1 class="fw-light">Clientside Image Preview</h1>
            <p class="lead text-muted">
                If you've ever wanted to have a clientside image preview 
                to help users see what they selected for upload.
            </p>
        </div>
    </div>
</section>

<div class="container">
    <div class="row">
        <div class="col">
            <div class="p-4 border border-primary">
                <h2 class="mb-5 border-bottom pb-3">Upload Avatar</h2>
                <form>
                    <input type="file" name="image" class="form-control" accept="image/*"
                           onchange="updatePreview(this, 'image-preview')" >
                    <div class="text-end">
                        <button class="btn btn-primary mt-3 ">Upload</button>
                    </div>
                </form>
            </div>
        </div>
        <div class="col">
            <div class="p-4 border border-secondary">
                <h2 class="border-bottom pb-3 mb-4">Avatar Preview</h2>
                <div class="text-center">
                    <img id="image-preview" 
                         src="https://via.placeholder.com/400"
                         style="width:400px"
                         class="rounded rounded-circle" alt="placeholder">
                </div>
            </div>
        </div>
    </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js"></script>
<script type="text/javascript">
    function updatePreview(input, target) {
        let file = input.files[0];
        let reader = new FileReader();
        
        reader.readAsDataURL(file);
        reader.onload = function () {
            let img = document.getElementById(target);
            // can also use "this.result"
            img.src = reader.result;
        }
    }
</script>
</body>
</html>

Conclusion

With a tiny bit of HTML and client-side JavaScript, you can create a better user experience for your users. I hope you enjoyed this short post, and if you found it helpful, please share it with your friends and coworkers.