Image Optimization with Next.js, AWS Amplify, and Amazon S3
Published: 2022-11-28 11:57:59

# Image Optimization with Nextjs 13 and Amplify Storage
Nextjs is arguably the hottest JavaScript framework today, with many awesome features that make it desirable for front-end engineers. Of all its amazing features, one that stands out is the `Image` component and image optimization. That’s not surprising, considering that front-end developers have always struggled with correctly implementing images on the web. The Nextjs Image component has some cool features that make it easier for developers to manage and optimize images in applications.

This article will teach us how to use some image optimization features.  We will also get practical and demonstrate a scenario where we use AWS Amplify Storage (backed by AWS S3) to store and retrieve the image we want to optimize; instead of using local (static) images. We will also learn how to deploy a Nextjs 13 app to Amplify hosting. 


## What You Will Learn

Before we get to writing code, let’s highlight some of the things you will learn from this article:


- **Fill dimensions of the parent element:** When accessing images from a remote source without knowing the image width and height, you should use the `fill` property. `fill` is a boolean property that allows the image to “fill” the parent element without explicitly setting `width` and `height`. However, you must set a `position` property. By default, it’s set to `position: absolute`. 


- **Blur-up placeholder:** It’s generally good UX to have a placeholder shimmer, or blur image when loading images. Nextjs Image component has a `placeholder` property that accepts a `blur` value. When accessing a dynamic image, you have to set the `blurDataURL` property.


- **Responsive Image:** Making images responsive to all screen sizes can be a legitimate cause of headaches. It’s recommended that you use `srcset`  and have different image sizes to make it responsive to all screens.  Nextjs Image automatically generates image source sets. You have to use the `sizes` property to define the viewport width. For instance:


    sizes="(max-width: 768px) 100vw,(max-width: 1200px) 50vw, 33vw"


- **Text on background image:** Placing text on a background image can be tricky most times but with the combo of `placeholder`, `fill` , and some CSS styling, we can implement it with the `Image` component. 

Amplify already supports the latest version of Nextjs (13), which means you can confidently deploy all the above-listed features. 

## Pre-requisites

You need to have the following to follow through:

- Nodejs ≥v14 installed (with npm or yarn)
- Knowledge of JavaScript and React
- A code editor (preferably VS code)
- AWS Amplify CLI installed
    yarn add @aws-amplify/cli --global
- AWS Amplify configured
    amplify configure

To code along, check out the [Github](https://github.com/Djcodebeast/next-amplify-image) repo here.

## Create a Next.js Project

Let’s get started by bootstrapping a nextjs project. Run this command on your terminal:
```bash
yarn create next-app
```
When this is done, open the project in your code editor. 
```bash
cd project-name
code . (for vs code users)
```

## Set up Amplify Storage

(Jump down to [Implementing the Nextjs Image](#retrieving-objects-from-the-bucket) if your image is already hosted and you have the URL)

Install the `aws-amplify` package:
```bash
yarn add aws-amplify
```
Next, initialize the Amplify backend on the project. Run this command on your integrated terminal
```bash
# Amplify CLI requireed. See prerequisites above
amplify init
```
This command will also create a `src/aws-exports.js` file in the root directory. Go to `pages/_app.js` and modify the code to this:
```js

    import { Amplify } from 'aws-amplify';
    import awsconfig from '../src/aws-exports';
    import Layout from '../src/components/Layout';
    
    Amplify.configure({
      ...awsconfig, ssr: true
    });
    function MyApp({ Component, pageProps }) {
      return (      
        <Component {...pageProps} />
      )
    }
    export default MyApp
```

When this is done, run this command to add Amplify Storage. 
```bash
amplify add storage
```
You’ll be prompted with the following options to select
```bash
? Select from one of the below mentioned services: (Use arrow keys)
❯ Content (Images, audio, video, etc.)
    NoSQL Database
```
Choose `Content (Images, audio, video, etc.)`.
```bash
? You need to add auth (Amazon Cognito) to your project in order to add storage for user files. Do you want to add auth now? (Y/n) ›
```
Choose `Yes`
```bash
? Provide a friendly name for your resource that will be used to label this category in the project: › nextimageresource
? Provide bucket name: › nextimageproject365
```
You can use any name of your choice. 
```bash
? Who should have access: …  (Use arrow keys or type to filter)
    Auth users only
❯ Auth and guest users
```
You want Auth and guest users to have access to your Storage bucket. 
```bash
? What kind of access do you want for Authenticated users? …  (Use arrow keys or type to filter)
    ✔ create/update
    ✔ read
❯✔ delete
```
We want to give Auth users all the access they need. 
```bash
? What kind of access do you want for Guest users? …  (Use arrow keys or type to filter)
    ✔ create/update
    ✔ read
❯✔ delete
```
Also, give full access to unauthenticated users (for this article).

Finally,
```bash
✔ Do you want to add a Lambda Trigger for your S3 Bucket? (y/N) · no
```
Choose `no` 

Let’s now go ahead and push this to the cloud. Run this command:
```bash
amplify push
```

When this is done, run this command on your terminal to open your project dashboard on Amplify.
```bash
amplify console

? Which site do you want to open? …  (Use arrow keys or type to filter)
❯ Amplify Studio
    AWS console
```
Choose Amplify Studio. This will open the studio on your default browser.



## Uploading an Image
![](https://paper-attachments.dropboxusercontent.com/s_5AA019372643D6460DD99238E7B5E3879ACA7FDE4344AD2C5F8C86426DEA8E45_1669298389515_Screenshot+2022-11-24+at+14.59.27.png)


Click on the File browser link in the left sidebar. 

![](https://paper-attachments.dropboxusercontent.com/s_5AA019372643D6460DD99238E7B5E3879ACA7FDE4344AD2C5F8C86426DEA8E45_1669298486070_Screenshot+2022-11-24+at+15.00.24.png)


Click on the **public** folder. All files here will be accessible by anybody that uses your app. Go ahead and upload an image.

Click on the checkbox and copy the JavaScript code at the bottom ride sidebar. This code snippet returns the full image URL with appropriate permissions.

![](https://paper-attachments.dropboxusercontent.com/s_5AA019372643D6460DD99238E7B5E3879ACA7FDE4344AD2C5F8C86426DEA8E45_1669298616860_Screenshot+2022-11-24+at+15.03.27.png)


Head to `next.config.js` and update it with these lines of code:
```js
module.exports = {
    reactStrictMode: true,
    images: {
    remotePatterns: [
        {
        protocol: 'https',
        hostname: '[bucketname-dev].s3.amazonaws.com',
        port: '',
        pathname: '/public/**',
        },
    ],
    },
}
```
Replace `[``bucketname-dev``]` with your bucket name. It should end with `-dev`.


## Retrieving the Image from Storage

To retrieve the image we just uploaded, update the `index.js` with these lines of code.
```js
// code snippet from https://github.com/Djcodebeast/next-amplify-image/blob/main/pages/index.js
import { Storage } from 'aws-amplify';
import { useState, useEffect } from 'react'
...
const [images, getImages] = useState([])
const getUploadedImage = async () => {
    const file = await Storage.get("pexels-marina-leonova-9465701.png", {
        level: "public"
    });
    getImages(file)
}
useEffect(() => {
    getUploadedImage()
}, [])
...
```

The `get()` method accepts two arguments; the first one (not optional) is the prefix. For instance, if you uploaded the images to a **photos** folder in the s3 bucket, the prefix will be `/photos`. The other argument is an object that can accept a range of properties, including the `level` of protection. 

Let’s render the image using Next Image component.
```jsx
<div>
    <Image
    src={images}
    alt="Picture of the author"
    width={300}
    height={300}
    unoptimized={true}
    />
</div>
```
This is an unoptimized image. It’s equivalent to the  `<img />` element. Let’s go ahead and implement the features we discussed earlier.


## Implementing the Nextjs Image Optimization features

**Fill dimensions of the parent element:**

[Code snippet](https://github.com/Djcodebeast/next-amplify-image/blob/main/pages/fill.js)

```jsx
<div className="relative w-[300px] h-[500px] border-2 border-red-400 my-8 overflow-hidden">
    <Image
        alt="Layout Fill"
        src={imageUrl}
        fill
        style={{
            objectFit: 'cover',
        }}
    />
</div>
<div className="relative w-[300px] h-[500px] border-2 border-red-400 my-8">
    <Image
        alt="Layout Fill"
        src={imageUrl}
        fill
        style={{
            objectFit: 'contain',
        }}
    />
</div>

<div className="relative w-[300px] h-[500px] border-2 border-red-400 my-8">
    <Image
        alt="Layout Fill"
        src={imageUrl}
        quality={100}
        fill
        style={{
            objectFit: 'none',
        }}
    />
</div>
```
**Blur-up placeholder**
This will add a blur placeholder while the image is loading. To generate a data URL for an image, you can use a generator tool like [this](https://dopiaza.org/tools/datauri/index.php).

[Code snippe](https://github.com/Djcodebeast/next-amplify-image/blob/main/pages/placeholder.js)[t](https://github.com/Djcodebeast/next-amplify-image/blob/main/pages/placeholder.js)

```jsx
<Image
    alt="Placeholder Blur"
    src={imageUrl}
    placeholder="blur"
    blurDataURL="data:application/xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48RXJyb3I+PENvZGU+SHR0cFZlcnNpb25Ob3RTdXBwb3J0ZWQ8L0NvZGU+PE1lc3NhZ2U+VGhlIEhUVFAgdmVyc2lvbiBzcGVjaWZpZWQgaXMgbm90IHN1cHBvcnRlZC48L01lc3NhZ2U+PFJlcXVlc3RJZD43MkQ4NUVCQkMxQjg3QUVGPC9SZXF1ZXN0SWQ+PEhvc3RJZD5FdWxFc05sTWVLYnBHNStSVlc1bWFFTWlENzJNQ1pCTW8zbytGWmJuVnBYVVJrV1RQZkxoZC9iSWpoa0pUWDJ3czBOSVJQQVcyNGY1U3BwdUNEVkQwK25qQVkvbDNsVDQ8L0hvc3RJZD48L0Vycm9yPg=="
    width={700}
    height={475}
    style={{
        maxWidth: '100%',
        height: 'auto',
    }}
/>
```
**Responsive Image**

[Code snippet](https://github.com/Djcodebeast/next-amplify-image/blob/main/pages/responsive.js)

```jsx
<Image
    alt="layout Responsive"
    src={imageUrl}
    width={700}
    height={475}
    sizes="(max-width: 768px) 100vw,(max-width: 1200px) 50vw, 33vw"
    style={{
        width: '100%',
        height: 'auto',
    }}
/>
```
**Text on a background image**

[Code snippet](https://github.com/Djcodebeast/next-amplify-image/blob/main/pages/background.js)

```jsx
<Image
    alt="Blur Background"
    src={imageUrl}
    placeholder="blur"
    blurDataURL={`data:application/xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48RXJyb3I+PENvZGU+SHR0cFZlcnNpb25Ob3RTdXBwb3J0ZWQ8L0NvZGU+PE1lc3NhZ2U+VGhlIEhUVFAgdmVyc2lvbiBzcGVjaWZpZWQgaXMgbm90IHN1cHBvcnRlZC48L01lc3NhZ2U+PFJlcXVlc3RJZD43MkQ4NUVCQkMxQjg3QUVGPC9SZXF1ZXN0SWQ+PEhvc3RJZD5FdWxFc05sTWVLYnBHNStSVlc1bWFFTWlENzJNQ1pCTW8zbytGWmJuVnBYVVJrV1RQZkxoZC9iSWpoa0pUWDJ3czBOSVJQQVcyNGY1U3BwdUNEVkQwK25qQVkvbDNsVDQ8L0hvc3RJZD48L0Vycm9yPg==`}
    className="fixed min-h-screen max-w-[100vw] overflow-hidden z-[-1]"
    quality={100}
    fill
    style={{
        objectFit: 'cover',
    }}
/>
<p className="m-0 text-2xl leading-10 text-center pt-[40vh] text-white drop-shadow-lg">
    Image Component
    <br />
    as a Background
</p>
```

## Deploy App to Amplify 

Amplify Hosting supports all Next.js features, including the new Next.js 13 features. Let’s walk through the steps to deploy your app.
 
Ensure you’ve created and pushed your code to GitHub or your preferred Git provider. 

Navigate to your AWS console and the amplify dashboard for the project we created. 

![](https://paper-attachments.dropboxusercontent.com/s_5AA019372643D6460DD99238E7B5E3879ACA7FDE4344AD2C5F8C86426DEA8E45_1669211662308_Screenshot+2022-11-23+at+14.54.06.png)


Click on the **Hosting environments** tab.

Next, choose your Git provider (Github, in my case) and click on the Connect branch button. Ensure you install and authorize AWS Amplify to access your repositories.


![](https://paper-attachments.dropboxusercontent.com/s_5AA019372643D6460DD99238E7B5E3879ACA7FDE4344AD2C5F8C86426DEA8E45_1669211862114_Screenshot+2022-11-23+at+14.57.29.png)


Click on the Next button. 
You’ll be redirected to this page.

![](https://paper-attachments.dropboxusercontent.com/s_5AA019372643D6460DD99238E7B5E3879ACA7FDE4344AD2C5F8C86426DEA8E45_1669212086903_Screenshot+2022-11-23+at+15.00.52.png)


Choose **dev** as Environment and click on the **Create new role.**
You’ll be redirected to an **IAM** screen in the next tab. Click on the **Next: Permissions** button. Do the same for the rest of the screens. 

![](https://paper-attachments.dropboxusercontent.com/s_5AA019372643D6460DD99238E7B5E3879ACA7FDE4344AD2C5F8C86426DEA8E45_1669212358580_Screenshot+2022-11-23+at+15.04.40.png)


Click on the **Create role** button. 
Head back to the Build Settings tab. 

![](https://paper-attachments.dropboxusercontent.com/s_5AA019372643D6460DD99238E7B5E3879ACA7FDE4344AD2C5F8C86426DEA8E45_1669212468109_Screenshot+2022-11-23+at+15.06.55.png)


 Select the `**amplifyconsole-backend-role**` you just created. If you can’t find it, click on the refresh button. 

![](https://paper-attachments.dropboxusercontent.com/s_5AA019372643D6460DD99238E7B5E3879ACA7FDE4344AD2C5F8C86426DEA8E45_1669212550445_Screenshot+2022-11-23+at+15.08.57.png)


Review and click on **Save and deploy.**

![](https://paper-attachments.dropboxusercontent.com/s_5AA019372643D6460DD99238E7B5E3879ACA7FDE4344AD2C5F8C86426DEA8E45_1669212617595_Screenshot+2022-11-23+at+15.09.58.png)


 Sit back and watch your app get deployed. This might take a few minutes, depending on your internet connection.

## Conclusion

In this article, we learned about  Nextjs Image optimization features and using Amplify Storage. We also learned how to upload and retrieve Storage bucket objects programmatically. 
Then we deployed our Nextjs 13 frontend and Amplify backend to Amplify hosting.