Responsive Images with AWS Serverless Image Handler (S3) for VueJS/React

September 4, 2022
aws serverless image handlerresponsive imagesaws lambda

AWS offers a cheap and easy image resizing solution for image files stored in an S3 bucket. The Serverless Image Handler uses Cloudfront, S3, and Sharp to dynamically resize and format source images through URL manipulation. This tutorial goes over how to use it and integrate the solution seamlessly into a Vue or React project.

How it works

The Image Handler lets you modify images in an S3 bucket by sending modification directions in the URL. That means images can be resized dynamically on demand as needed. Because of the CloudFront integration the images are heavily cached reducing the need to perform the modification which saves resources and makes it much cheaper than resizing services.

cloud formation diagram

Installation

  1. First, create a new AWS CloudFormation stack from the AWS Serverless Image Handler template. This can be done by navigating to the CloudFormation console in the AWS Management Console and clicking on "Create Stack". Use the most recent template file from AWS.
  2. Once the stack is created, go to the AWS Lambda console and find the serverless-image-handler function. Open the function and scroll down to the environment variables section. Add a new environment variable named "BUCKET" and set its value to your bucket name.
  3. Finally, navigate to the Amazon API Gateway console and find the "serverless-image-handler" API. Open the API and click on "Stages". Select the "prod" stage and click on "Export". This will give you an API endpoint URL that you can use to access the image handler.

The implementation guide has detailed installation instructions and security considerations for the configuration.

Vue Implementing with a Mixin (Gridsome)

To implement the Image Handler in Vue/Gridsome add a mixin. Mixins are helper functions that you can use throughout your app. For basic resizing this mixin should take the original bucket URL, resizing information and optionally a target format (for converting to webp).

  
//  /src/mixins.resp.js

export default {
  methods: {
    resp: function(ourl, width, height=false, ext=false) {
    
      var handlerHost = 'https://image-resizing-distro-url.cloudfront.net/';
      var originalHost = 'https://original-host.url/';

      // Remove the original host from the S3 url to get the path to the image.
      var key = ourl.replace(originalHost, '');
    
      if (ourl == undefined) {
        return '';
      }
    
      // Skip resizing .gif files for the moment.
      if (ourl.indexOf('.gif') !== -1) {
        return ourl;
      }
    
      // Check if this should use the original extension.
      if (!ext) { // If ext is set, use that.
        ext = ourl.split('.').pop();
      }
    
      var params = {
        "bucket": "BUCKET-NAME-HERE",
        "key": decodeURIComponent(key),
        "edits": {
          "resize": {
            "fit": "cover"
          },
          "toFormat": ext,
        }
      };
    
      if (width) {
        params["edits"]["resize"]["width"] = width;
      }
    
      if (height) {
        params["edits"]["resize"]["height"] = height;
      }
    
      var paramsJSON = Buffer.from(JSON.stringify(params)).toString('base64');
      return `${handlerHost}${paramsJSON}`;
    }
  }
}