Generating Image Thumbnails in the Browser using JavaScript and FilePond
Tags: javascript, webdev, opensource, html
Published: 2019-06-14 07:54:23

---
title: Generating Image Thumbnails in the Browser using JavaScript and FilePond
published: true
description: How to configure FilePond so that it automatically generates thumbnails of images before they are uploaded to the server.
cover_image: https://thepracticaldev.s3.amazonaws.com/i/2izgfyov79fm21lcefha.png
tags: javascript, webdev, opensource, html
---

[FilePond](https://pqina.nl/filepond/) is a free **JavaScript File Upload Library**. In this article we'll explore the functionality of FilePond and how it can be extended with plugins. We'll combine a handful of these plugins to generate image thumbnails on the client.

If you want to code along, open an empty HTML file in your favourite text editor.

Let's get started.

*In a hurry? [View the end result here](https://codepen.io/rikschennink/pen/ydYxOE)*


## Setting up FilePond

We'll start with a basic HTML outline and add an `<input type="file"/>`.

*Please note that you can also `import` and [use FilePond as an ES6 module](https://pqina.nl/filepond/docs/patterns/installation/#using-npm), but for this tutorial we'll stick to plain HTML as it requires less project setup*

```html
<!doctype html>
<html>
  <head>
    <title>Hello World</title>
  </head>
  <body>
    <input type="file">
  </body>
</html>
```


Let's add the required FilePond scripts and styles.

```html
<!doctype html>
<html>
  <head>
    <title>Hello World</title>
    
    <!-- FilePond styles -->
    <link href="https://unpkg.com/filepond/dist/filepond.css" rel="stylesheet">

  </head>
  <body>
    <input type="file">

    <!-- FilePond scripts -->
    <script src="https://unpkg.com/filepond/dist/filepond.js"></script>

  </body>
</html>
```


And now we extend it with the FilePond initialisation logic.

```html
<!doctype html>
<html>
  <head>
    <title>Hello World</title>
    <link href="https://unpkg.com/filepond/dist/filepond.css" rel="stylesheet">
  </head>
  <body>
    <input type="file">
    <script src="https://unpkg.com/filepond/dist/filepond.js"></script>

    <!-- FilePond initialisation logic -->
    <script>
    const inputElement = document.querySelector('input[type="file"]');
    const pond = FilePond.create(inputElement);
    </script>

  </body>
</html>
```

This will tell FilePond to `create` a FilePond instance at the location of our file input.

If you run this in your browser the FilePond drop area will appear. It can handle a single file. We can add the `multiple` attribute to the `input` element to allow multiple files to be added.

```html
<!doctype html>
<html>
  <head>
    <title>Hello World</title>
    <link href="https://unpkg.com/filepond/dist/filepond.css" rel="stylesheet">
  </head>
  <body>

    <!-- Add 'multiple' attribute -->
    <input type="file" multiple>

    <script src="https://unpkg.com/filepond/dist/filepond.js"></script>
    <script>
    const inputElement = document.querySelector('input[type="file"]');
    const pond = FilePond.create(inputElement);
    </script>
  </body>
</html>
```

Okay, this is nice. Let's add some plugins.


## Adding Plugins

Let's add the [Image Preview](https://pqina.nl/filepond/docs/patterns/plugins/image-preview/), [Image Resize](https://pqina.nl/filepond/docs/patterns/plugins/image-resize/), and [Image Transform](https://pqina.nl/filepond/docs/patterns/plugins/image-transform/) plugins.

The Image Preview plugin will show a preview of a dropped image. The Image Resize plugin will add resize information to the FileItem metadata, and lastly the Image Transform plugin, it will use the resize information to resize the actual image.


```html
<!doctype html>
<html>
  <head>
    <title>Hello World</title>
    <link href="https://unpkg.com/filepond/dist/filepond.css" rel="stylesheet">

    <!-- Add plugin styles -->
    <link href="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css" rel="stylesheet">


  </head>
  <body>
    <input type="file" multiple>

    <!-- Add plugin scripts -->
    <script src="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.js"></script>
    <script src="https://unpkg.com/filepond-plugin-image-resize/dist/filepond-plugin-image-resize.js"></script>
    <script src="https://unpkg.com/filepond-plugin-image-transform/dist/filepond-plugin-image-transform.js"></script>

    <script src="https://unpkg.com/filepond/dist/filepond.js"></script>
    <script>
    const inputElement = document.querySelector('input[type="file"]');
    const pond = FilePond.create(inputElement);
    </script>
  </body>
</html>
```

Refresh the page, nothing changed. Weird. For FilePond to use the plugins we need to register them with the library, this is not done automatically.

Let's extend our bottom initialisation `<script>` like shown below.

```js
// register the plugins with FilePond
FilePond.registerPlugin(
  FilePondPluginImagePreview,
  FilePondPluginImageResize,
  FilePondPluginImageTransform
);

const inputElement = document.querySelector('input[type="file"]');
const pond = FilePond.create(inputElement);
```

To see this working, drop an image on the FilePond drop area, it now shows a preview of the image.


## Showing a Resized Preview

It's now time to tell FilePond of our intentions. We can do this by passing a configuration object to the `FilePond.create` method.

```js
const pond = FilePond.create(inputElement, {
  imageResizeTargetWidth: 256
});
```

This will instruct the resize plugin to add a `resize` entry to the FileItem metadata. We can view this metadata by adding the `onaddfile` callback.

```js
const pond = FilePond.create(inputElement, {
  imageResizeTargetWidth: 256,

  // add onaddfile callback
  onaddfile: (err, fileItem) => {
    console.log(err, fileItem.getMetadata('resize'));
  }

});
```

The following will be logged to the developer console.

```
null, { mode: "cover", upscale: true, size: { width: 256, height: 256 } }
```

`null` means that there was no error when adding the file, and the rest of the data is related to the `resize` information added by the Image Resize plugin.

Let's now show the output of the Image Transform plugin. We can do this by adding the `onpreparefile` callback, it's called when the Image Transform plugin has "prepared" a file. It receives both the `fileItem` and the `output` Blob object of the Image Transform process.

```js
const pond = FilePond.create(inputElement, {
  imageResizeTargetWidth: 256,
  onaddfile: (err, fileItem) => {
    console.log(err, fileItem.getMetadata('resize'));
  },

  // add onpreparefile callback
  onpreparefile: (fileItem, output) => {
    // create a new image object
    const img = new Image();

    // set the image source to the output of the Image Transform plugin
    img.src = URL.createObjectURL(output);

    // add it to the DOM so we can see the result
    document.body.appendChild(img);
  }

});
```

The resized image now appears on the page below the FilePond drop area.

It should be 256 pixels wide, and depending on the aspect ratio of the image its height might exceed 256 pixels. That's because `imageResizeMode` is set to `'cover'`, setting it to `'contain'` will make sure that the output image is always contained inside the resize target dimensions.

Let's set `imageResizeMode` to `'contain'` now.


```js
const pond = FilePond.create(inputElement, {
  imageResizeTargetWidth: 256,

  // set contain resize mode
  imageResizeMode: 'contain',

  onaddfile: (err, fileItem) => {
    console.log(err, fileItem.getMetadata('resize'));
  },
  onpreparefile: (fileItem, output) => {
    const img = new Image();
    img.src = URL.createObjectURL(output);
    document.body.appendChild(img);
  }

});
```

Alright, we've accomplished generating a single thumbnail, now let's generate multiple.




## Generating Multiple Thumbnails

The Image Transform plugin has a couple configuration values of its own. 

By setting the `imageTransformOutputQuality` property we can control the image output quality and we can convert images to JPEGs by setting the `imageTransformOutputMimeType` to `'image/jpeg'`

The property we need now is `imageTransformVariants`, it's there to create additional versions of a file. We'll generate two additional versions of the image, one 512 pixels wide, and one 64 pixels wide.

```js
const pond = FilePond.create(inputElement, {
  imageResizeTargetWidth: 256,
  imageResizeMode: 'contain',

  // add imageTransformVariant settings
  imageTransformVariants: {
    thumb_medium_: transforms => {
      transforms.resize.size.width = 512;
      return transforms;
    },
    thumb_small_: transforms => {
      transforms.resize.size.width = 64;
      return transforms;
    }
  },

  onaddfile: (err, fileItem) => {
    console.log(err, fileItem.getMetadata('resize'));
  },
  onpreparefile: (fileItem, output) => {
    const img = new Image();
    img.src = URL.createObjectURL(output);
    document.body.appendChild(img);
  }
});
```

Nuts! Our script throws an error. 

```
Failed to execute 'createObjectURL' on 'URL': No function was found that matched the signature provided.
```

It's telling us that whatever we're trying to create a URL to, it's not working.

This is because we're now generating multiple files. Our `output` parameter in the `onpreparefile` callback has changed to an array. Let's alter the code so it can deal with a list of files.

```js
const pond = FilePond.create(inputElement, {
  imageResizeTargetWidth: 256,
  imageResizeMode: 'contain',
  imageTransformVariants: {
    thumb_medium_: transforms => {
      transforms.resize.size.width = 512;
      return transforms;
    },
    thumb_small_: transforms => {
      transforms.resize.size.width = 64;
      return transforms;
    }
  },
  onaddfile: (err, fileItem) => {
    console.log(err, fileItem.getMetadata('resize'));
  },
  
  // alter the output property
  onpreparefile: (fileItem, outputFiles) => {
    // loop over the outputFiles array
    outputFiles.forEach(output => {
      const img = new Image();

      // output now is an object containing a `name` and a `file` property, we only need the `file`
      img.src = URL.createObjectURL(output.file);

      document.body.appendChild(img);
    })
  }

});
```

Dropping a file now result in three images being added to the DOM, all matching the widths provided. 

We can take this further by adding the [Image Crop plugin](https://pqina.nl/filepond/docs/patterns/plugins/image-crop/), we can then tell FilePond to automatically crop the output images in certain aspect ratios.

Let's do this quickly and then call it a day.


```html
<!doctype html>
<html>
  <head>
    <title>Hello World</title>
    <link href="https://unpkg.com/filepond/dist/filepond.css" rel="stylesheet">

    <!-- Add plugin styles -->
    <link href="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.css" rel="stylesheet">


  </head>
  <body>
    <input type="file" multiple>

    <script src="https://unpkg.com/filepond-plugin-image-preview/dist/filepond-plugin-image-preview.js"></script>
    <script src="https://unpkg.com/filepond-plugin-image-resize/dist/filepond-plugin-image-resize.js"></script>
    <script src="https://unpkg.com/filepond-plugin-image-transform/dist/filepond-plugin-image-transform.js"></script>
    
    <!-- add the Image Crop plugin script -->
    <script src="https://unpkg.com/filepond-plugin-image-crop/dist/filepond-plugin-image-crop.js"></script>


    <script src="https://unpkg.com/filepond/dist/filepond.js"></script>
    <script>
    FilePond.registerPlugin(
      // register the Image Crop plugin with FilePond
      FilePondPluginImageCrop,
      FilePondPluginImagePreview,
      FilePondPluginImageResize,
      FilePondPluginImageTransform
    );

    const inputElement = document.querySelector('input[type="file"]');
    const pond = FilePond.create(inputElement, {
      // add the Image Crop default aspect ratio
      imageCropAspectRatio: 1,
      imageResizeTargetWidth: 256,
      imageResizeMode: 'contain',
      imageTransformVariants: {
        thumb_medium_: transforms => {
          transforms.resize.size.width = 512;

          // this will be a landscape crop
          transforms.crop.aspectRatio = .5;

          return transforms;
        },
        thumb_small_: transforms => {
          transforms.resize.size.width = 64;
          return transforms;
        }
      },
      onaddfile: (err, fileItem) => {
        console.log(err, fileItem.getMetadata('resize'));
      },
      onpreparefile: (fileItem, outputFiles) => {
        outputFiles.forEach(output => {
          const img = new Image();
          img.src = URL.createObjectURL(output.file);
          document.body.appendChild(img);
        })
      }
    });
    </script>
  </body>
</html>
```

You can view a live demo below. Drop an image file and you'll see three different output files.

- A big square measuring 256 x 256 pixels.
- A landscape rectangle with a width of 512 pixels and a height of 256 pixels.
- A tiny 64x64 pixel square.

{% codepen https://codepen.io/twhite96/pen/ydYxOE default-tab=result %}

Images are resized on a separate thread so the image generation process is fairly quick and doesn't block the user interface.

We could now use the [Image Filter plugin](https://pqina.nl/filepond/docs/patterns/plugins/image-filter/) to also generate a grayscale version of the image by applying a CoorMatrix to each pixel, but I think we've got our bases covered for now. We've learned how to generate thumbnails on the client and how to leverage FilePond to do so.

I hope this has been useful to you, do let me know if you have any questions, I'm happy to answer them below.