Animating Next.js Images With Framer Motion

Eugene Musebe

BACKGROUND

An image carousel is a great way to showcase specific images on your website. This can enhance the overall visual appearance of your website and also improve the general user experience.

In this article, you'll learn how you can prototype a simple image carousel using Cloudinary, Next.js, and Framer-Motion as illustrated in the Codesandbox below.

Prerequisites

Before getting started, ensure that you have the following:

Project Setup

To get started with Next.JS, navigate to the project directory of your choice and run:

1npx create-next-app carousel

The command will set up everything automatically for you. After the installation is complete, follow the instructions on your terminal to start the development server.

Set Up Dependencies

Install the following dependencies in your project:

1npm i cloudinary react-icons framer-motion

The carousel will use the Cloudinary package to interact with the images stored in cloudinary while react icons will be used to display image navigation arrows on the carousel. The framer motion package will be used to animate the fetched images.

As we shall be using cloudinary for retrieval of the carousel images, create a .env.local file in the root of the project directory. This is where we shall store all our environment variables configurations. Content stored in this file are not included in the browser build. This file should always be included in your gitignore file as it stores sensitive information.

To the file add the following keys :

1CLOUDINARY_CLOUD_NAME=
2CLOUDINARY_API_KEY=
3CLOUDINARY_API_SECRET=

Their respective values will be acquired from the Cloudinary dashboard.

Cloudinary setup

Once you have created an account on Cloudinary, copy your cloud_name, api_key & api_secret from the dashboard, and add them as values to the respective keys in the .env.local file created in the application.

The next step is to create a folder where all of the carousel images will be stored. Click on the Media Library tab, and create a folder named Carousel. We will programmatically access the contents of the folder.

The last step is to upload the images you want to see on the carousel to the folder. For demo purposes, you can download free stock photos from Pexels.

To interact with the images stored in Cloudinary from the application, create a utils folder in the root project structure of your application, and add a cloudinary.js file in it.

This file will be used to store all the snippets that you will use throughout the application to interact with Cloudinary.

Paste the following to the file:

1import cloudinary from 'cloudinary';
2
3cloudinary.config({
4 cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
5 api_key: process.env.CLOUDINARY_API_KEY,
6 api_secret: process.env.CLOUDINARY_API_SECRET,
7});
8}

I imported the Cloudinary package from the code above and configured the required configuration keys to access all the assets stored in Cloudinary. By using process.env, one can access values stored in the .env.local file.

The next step is to create an asynchronous function that will fetch all images from Cloudinary:

1export async function getAllImages() {
2
3 const response = await cloudinary.v2.api.resources({
4 type: 'upload',
5 prefix: 'Carousel',
6 });
7
8 const sliderData = response.resources.map((image, key) => ({
9 id: key,
10 ...image,
11 }));
12
13 return sliderData;
14}

By using cloudinary.v2.api.resources, one can manage and access all the resources (images) stored in the Carousel folder. After querying the Images stored in the folder, convert the JSON response received into an array of objects for easy image mapping when it comes to fetching data on the frontend.

The last configuration we need to make, to help the Next.JS application communicate with Cloudinary effectively, is to create a next.config.js file in the root project structure, and add the following to it:

1module.exports = () => {
2 return {
3 images: {
4 domains: ['res.cloudinary.com'],
5 },
6 };
7};

This will allow us to let Cloudinary optimize images instead of using Next's built-in image optimization.

With all the above setup and configurations, it's time to spin up the server.

Server setup

Next.js supports writing server-side code rendering out of the box. For this to happen, navigate to the page's directory, and create an API folder. Inside it, create a Cloudinary.JS file, and paste the following :

1import { getAllImages } from '../../utils/cloudinary';
2
3
4async function handler(req, res) {
5 if (req.method === 'GET') {
6 try {
7
8 const sliderData = await getAllImages();
9
10 res.status(200).json(sliderData);
11
12 } catch (error) {
13
14 res.status(500).json({ message: 'Getting images failed.' });
15 }
16 }
17}
18
19export default handler;

From the above, we first import the getAllImages function we created in the utils folder to be able to access all the images stored in Cloudinary. Then, we create an 'if' statement to define the method GET and fetch data from the defined endpoint. Then, create a try & catch statement where we assign the getAllImages function to the variable sliderData to fetch the data or catch all the errors that might occur in the process of fetching the images from Cloudinary. Lastly, we export the function so that it is globally available.

To test out if the endpoint works. visit your browser or postman, and make a GET request to endpoint http://localhost:3000/api/cloudinary. This should produce the following response :

Image Slider Component

To be able to consume the Api we created and show all the data to the application users, create a components folder on the root of the application and add an ImageSlider.js file to it. This will be a normal react component.

Component setup

This component will fetch all the images from the cloudinary and showcase them in an image slider. We shall use the Fetch API to perform data fetching from the API and reacts useState to set the images into the page state.

Create a functional component as follows:

1import React, { useState } from 'react';
2
3
4 let images = [];
5
6 fetch("/api/cloudinary")
7 .then((response) => response.json())
8 .then((data) => {
9 images = data.map((image) => {
10 return image.url;
11 });
12 );
13
14const ImageSlider = () => {
15const [[page, direction], setPage] = useState([0, 0]);
16const imageIndex = wrap(0, images.length, page);
17
18
19 return (
20
21 );
22};
23
24export default ImageSlider;

Above, we first declared images as an empty array after which we used the Fetch API to get all the images from Cloudinary and store them into the images variable.

Each fetched image will be displayed on a separate page hence storing all the pages in the application state. This will enable us to loop through the different images from the API once the application state changes.

To be able to navigate through the different Images, create a function that will shift the pages(image) based on the direction triggered as illustrated below :

1const paginate = (newDirection) => {
2 setPage([page + newDirection, newDirection]);
3 };

Then, create a variable that will store all the images based on their indexes so that the different image pages can be displayed on the Next.js image component:

1const src = images.length > 0 ? images[imageIndex] : `/amazon.jpg`;

NEXT.JS IMAGE OPTIMIZATION

To optimize how we load, optimize and resize images on various screen sizes, we utilized the image component which resizes all the carousel images on the fly.

We must first import the image component on top of the page to use it:

1import Image from 'next/image';

Create a div in the return statement and add the following code to display the various image pages based on their indexes:

1<Image
2 src={src}
3 alt="Picture of the author"
4 width={600}
5 height={600}
6 />

Animating The Image Carousel With Framer Motion

Framer motion is a powerful and easy-to-use library that can quickly help us achieve smooth image transition effects. It allows us to combine various animation types and serve as a solid foundation for making your pages even more user-friendly.

Importing the motion component and the other animation components that come with the library is the first step in using it. In our case, we'll make use of the AnimatePresence component.

Fontawesome imports will be used to create navigation icons.

1import { motion, AnimatePresence } from "framer-motion";
2import { FaAngleRight } from "react-icons/fa";

The next step is to wrap everything inside the return statement with the AnimatePresence component. By changing the different keys within the component it will create an Animated slideshow.

Create a div between the Animate presence component and Add the following to it :

1<AnimatePresence>
2<motion.div
3 className={styles.img}
4 key={page}
5 custom={direction}
6 initial={{ x: 300, opacity: 0 }}
7 animate={{ x: 0, opacity: 1 }}
8 exit={{ opacity: 0 }}
9 whileHover={{ scale: 1.1 }}
10 whileTap={{ scale: 0.9 }}
11 transition={{
12 x: { type: "spring", stiffness: 300, damping: 300 },
13 opacity: { duration: 1 }
14 }}
15>
16 </motion.div>
17</AnimatePresence>

Move the image component between the motion div for the transitions and animations applied to take effect on the optimized images.

Above we utilized transitions, whileHovers, whileTap, and the animate properties from framer motion to give the image carousel a smooth transition

We need to add back and forward navigation buttons below the AnimatePresence component to complete the component. This will be accomplished in the following ways:

1<motion.div
2 className={styles.next}
3 onClick={() => paginate(1)}
4 whileHover={{ scale: 1.2, transition: { duration: 0.5 } }}
5 whileTap={{ scale: 0.9 }}
6>
7 <FaAngleRight />
8</motion.div>
1<motion.div
2 className={styles.prev}
3 onClick={() => paginate(-1)}
4 whileHover={{ scale: 1.2, transition: { duration: 0.5 } }}
5 whileTap={{ scale: 0.9 }}
6>
7 <FaAngleRight />
8</motion.div>

By using the motion property on the button divs above we were also able to animate the navigation buttons when we hover and tap on them. We used Fontawesome to create the navigation signs inside the buttons.

Image Display

To display the ImageSlide.js component we just created, navigate to the pages folder and on the default index.js file, create a Homepage function and embed the image slider component as shown below.

1function HomePage({ sliderData }) {
2 return (
3 <Layout>
4 <ImageSlider/>
5 </Layout>
6 );
7}

Conclusion

You have successfully built an image carousel leveraging on Framer Motion, Next's massive capabilities such as out-of-the-box API & image component while using cloudinary as your image storage and manipulation tool.

Feel free to improve the application and make it better.

For reference visit:

Eugene Musebe

Software Developer

I’m a full-stack software developer, content creator, and tech community builder based in Nairobi, Kenya. I am addicted to learning new technologies and loves working with like-minded people.