Back to Blog Page
Frontend Development

Next Cloudinary – How to Integrate Cloudinary with Your Next.js Project

Next Cloudinary – How to Integrate Cloudinary with Your Next.js Project

Written by Kolade Chris | Jul 20, 2024 | #Next.js #Cloudinary | 7 minute Read

Integrating Cloudinary with your Next JS project simplifies image uploads by removing concerns about storage space and application size, streamlining asset management on your website in the process.

When you use Cloudinary, you can effectively handle image uploads and organize your media in the cloud. This setup boosts media management capabilities, as you can also crop, resize, and format images easily.

This guide will walk you through the steps to fully integrate Cloudinary into a Next JS application. You’ll learn how to upload images directly to Cloudinary and retrieve them for display in your Next JS projects.

How to Integrate Cloudinary with Your Next JS Project

Step 1: Create a Next JS Project with your Favourite Package Manager

Terminal window
1
npx create-next-app@latest # if you're using NPM
2
yarn create next-app # if you're using Yarn
3
pnpm create next-app # if you're using PNPM

For this demo, I have made the following choices in the prompts:

Next.js Installation Prompt choices

Step 2: Install the Next Cloudinary NPM Package and Cloudinary

Terminal window
1
npm install next-cloudinary cloudinary

We’ll use one of the components provided by the Next Cloudinary NPM package to display an upload button and the Cloudinary package to fetch the images after they’re uploaded.

Step 3: Get your Cloudinary Credentials and Save them in a .env File

You need the following credentials to be able to upload images and fetch images:

  • cloudinary cloud name
  • cloudinary API key
  • cloudinary API secret
  • cloudinary cloud preset name

To get the first three credentials in the list above, log in to your Cloudinary account and click “Programmable media” icon on the top-left corner. You’ll see them under “Product Environment Credentials”.

Programmable Media

Copy the three credentials and save them in a .env file like this:

Terminal window
1
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=
2
NEXT_PUBLIC_CLOUDINARY_API_KEY=
3
NEXT_PUBLIC_CLOUDINARY_API_SECRET=

You also need the cloud preset name so you can use any of the components available in the Next Cloudinary NPM package.

To get it, click on the gear icon in the bottom-left corner and select “Upload”: Upload Presets

Click the red link with the text “Enable unsigned uploading”:

Upload Presets

After that, you will see an upload preset name. Copy it and save it as the value of a NEXT_PUBLIC_CLOUDINARY_PRESET_NAME in your .env file:

Copy Upload Preset Name

The environment variables in your .env file should now look like this:

Terminal window
1
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=value
2
NEXT_PUBLIC_CLOUDINARY_PRESET_NAME=value
3
NEXT_PUBLIC_CLOUDINARY_API_KEY=value
4
NEXT_PUBLIC_CLOUDINARY_API_SECRET=value

Step 4: Add Cloudinary Domain to your next.config.mjs File

You must add the Cloudinary image domain, res.cloudinary.com to the images.remotePatterns array in your Next config file for the images to work.

Your next.config.mjs file should look like this after adding it:

1
/** @type {import('next').NextConfig} */
2
const nextConfig = {
3
images: {
4
remotePatterns: [
5
{
6
protocol: 'https',
7
hostname: 'res.cloudinary.com',
8
},
9
],
10
},
11
};
12
13
export default nextConfig;

Step 5: Use the Next Cloudinary Built-in Component in your Components

The Next Cloudinary package has built-in components with which you can add a ready-made upload widget or button to your components. The one I chose is CldUploadButton.

Create a components folder in the root and a CloudinaryUploader.jsx client component file inside it.

Inside the file, import the CldUploadButton component, use it in the return statement, and create a span element with the text Upload Image:

1
'use client';
2
import { CldUploadButton } from 'next-cloudinary';
3
4
const CloudinaryUploader = () => {
5
return (
6
<div>
7
<CldUploadButton>
8
<span>Upload Image</span>
9
</CldUploadButton>
10
</div>
11
);
12
};
13
14
export default CloudinaryUploader;

You need to pass the options and uploadPreset properties into the component. The value for the options should be { multiple: true } to allow multiple image upload, and that of the uploadPreset should be the cloud preset name from your .env file:

1
'use client';
2
import { CldUploadButton } from 'next-cloudinary';
3
4
const cloudPresetName = process.env.NEXT_PUBLIC_CLOUDINARY_PRESET_NAME;
5
6
const CloudinaryUploader = () => {
7
return (
8
<div>
9
<CldUploadButton
10
options={{ multiple: true }}
11
uploadPreset={cloudPresetName}
12
>
13
<span>Upload Image</span>
14
</CldUploadButton>
15
</div>
16
);
17
};
18
19
export default CloudinaryUploader;

Clear everything inside the page.jsx file in the app folder and create an h1 tag with the text Next JS Cloudinary Integration, then import the CloudinaryUploader component and use it in the return statement

1
import CloudinaryUploader from '@/components/CloudinaryUploader';
2
3
function Home() {
4
return (
5
<main>
6
<h1 className=" text-5xl text-center mt-4">
7
Next JS Cloudinary Integration
8
</h1>
9
10
<CloudinaryUploader />
11
</main>
12
);
13
}
14
15
export default Home;

At this point, the button looks like this:

Initial Upload Button

I will add some Tailwind classes to make the button and its text look better:

1
'use client';
2
3
import { CldUploadButton } from 'next-cloudinary';
4
5
const cloudPresetName = process.env.NEXT_PUBLIC_CLOUDINARY_PRESET_NAME;
6
7
const CloudinaryUploader = () => {
8
return (
9
<div className="flex items-center justify-center ">
10
<CldUploadButton
11
options={{ multiple: true }}
12
uploadPreset={cloudPresetName}
13
className="bg-green-400 py-2 px-3 rounded border mt-4 text-white
14
hover:bg-green-500 transition ease-in-out delay-200"
15
>
16
<span className="text-2xl">Upload Images</span>
17
</CldUploadButton>
18
</div>
19
);
20
};
21
22
export default CloudinaryUploader;

The button now looks like this:

Good Upload Button

If you click the button, a giant asset upload popup will appear

Good Upload Button

This means you can upload media through all those sources at the top – including Unsplash.

You can select which of those sources you want by passing a sources key with the sources in an array as the value into the options prop like this:

1
'use client';
2
3
import { CldUploadButton } from 'next-cloudinary';
4
5
const cloudPresetName = process.env.NEXT_PUBLIC_CLOUDINARY_PRESET_NAME;
6
7
const CloudinaryUploader = () => {
8
return (
9
<div className="flex items-center justify-center ">
10
<CldUploadButton
11
options={{
12
multiple: true,
13
sources: ['local', 'url', 'unsplash', 'camera'],
14
}}
15
uploadPreset={cloudPresetName}
16
className="bg-green-400 py-2 px-3 rounded border mt-4 text-white
17
hover:bg-green-500 transition ease-in-out delay-200"
18
>
19
<span className="text-2xl">Upload Images</span>
20
</CldUploadButton>
21
</div>
22
);
23
};
24
25
export default CloudinaryUploader;

How cool is that?

Here’s what that looks like when the upload button is clicked: Selected Upload Sources

At this point, If you have things set up correctly, you should be able to upload an image straight to Cloudinary.

Subscribe to my newsletter for coding tips, videos from reputable sources, articles from OG tech authors, and a ton of other goodies.

No BS. No fluff. Just pure software development goodies on a Sunday every week.

How to Query the Uploaded Images from Cloudinary and Display them

Now that you have the images uploaded to CLoudinary, let’s look at how you can display them.

You don’t need the useState or useEffect hook, and the fetch API to do this because the page.jsx file inside which we will perform the query is a server component. The cloudinary search API will do it for you.

Step 1: Create a Folder for the Uploaded Images to Go Into

You need to create a folder for the images so you can query the images with that folder name using the Cloudinary search API.

Go back and click the gear icon, “Upload”, and the Unisigned preset itself Click Unsigned Upload Preset

Type in a folder name right under “Folder” and click the “Save” button on the top-left corner:

Click Unsigned Upload Preset

Step 2: Create Cloudinary Configuration with the Cloudinary SDK (Software Development Kit)

Head back to the the page.jsx file inside the app directory and import v2 as cloudinary from the Cloudinary NPM package, then create a configuration with your cloud name, API key, and API secret:

1
import CloudinaryUploader from '@/components/CloudinaryUploader';
2
3
import { v2 as cloudinary } from 'cloudinary';
4
5
cloudinary.config({
6
cloud_name: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,
7
api_key: process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY,
8
api_secret: process.env.NEXT_PUBLIC_CLOUDINARY_API_SECRET,
9
});
10
11
function Home() {
12
return (
13
<main>
14
<h1 className=" text-5xl text-center mt-4">
15
Next JS Cloudinary Integration
16
</h1>
17
18
<CloudinaryUploader />
19
</main>
20
);
21
}
22
23
export default Home;

You can also put the configuration in a separate file if you want.

Step 3: Fetch the Images from the Folder Using the search API

Turn the Home component to an async function, create an images constant, and await on cloudinary.search(). To get the images from that folder, you need to chain .expression(folder-name) and then the execute() function.

You can then log images.resources to the console to see the images you’ve uploaded

1
import CloudinaryUploader from '@/components/CloudinaryUploader';
2
import { v2 as cloudinary } from 'cloudinary';
3
4
cloudinary.config({
5
cloud_name: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,
6
api_key: process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY,
7
api_secret: process.env.NEXT_PUBLIC_CLOUDINARY_API_SECRET,
8
});
9
10
async function Home() {
11
const images = await cloudinary.search
12
.expression('next-cloudinary')
13
.execute();
14
15
console.log(images.resources);
16
return (
17
<main>
18
<h1 className=" text-5xl text-center mt-4">
19
Next JS Cloudinary Integration
20
</h1>
21
22
<CloudinaryUploader />
23
</main>
24
);
25
}
26
27
export default Home;

This means you can destructure resources directly from that query if you want.

Step 4: Display the Images using the Image Component

Import Image from Next Image, map through image.resources inside the return statement, then pass in the appropriate props and values for the Image component

1
import CloudinaryUploader from '@/components/CloudinaryUploader';
2
import { v2 as cloudinary } from 'cloudinary';
3
import Image from 'next/image';
4
5
cloudinary.config({
6
cloud_name: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,
7
api_key: process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY,
8
api_secret: process.env.NEXT_PUBLIC_CLOUDINARY_API_SECRET,
9
});
10
11
async function Home() {
12
const images = await cloudinary.search
13
.expression('next-cloudinary')
14
.execute();
15
16
return (
17
<main>
18
<h1 className=" text-5xl text-center mt-4">
19
Next JS Cloudinary Integration
20
</h1>
21
22
<CloudinaryUploader />
23
24
{images.total_count > 0 &&
25
images.resources.map((image) => (
26
<div key={image.asset_id}>
27
<Image
28
src={image.secure_url}
29
height={image.height}
30
width={image.width}
31
alt="My cloudinary image"
32
priority
33
/>
34
</div>
35
))}
36
</main>
37
);
38
}
39
40
export default Home;

At this point, the images don’t look good at all

Bad looking images

So, I’ll go ahead and add some Tailwind classes to the page

1
import CloudinaryUploader from '@/components/CloudinaryUploader';
2
import { v2 as cloudinary } from 'cloudinary';
3
import Image from 'next/image';
4
5
cloudinary.config({
6
cloud_name: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,
7
api_key: process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY,
8
api_secret: process.env.NEXT_PUBLIC_CLOUDINARY_API_SECRET,
9
});
10
11
async function Home() {
12
const images = await cloudinary.search
13
.expression('next-cloudinary')
14
.execute();
15
16
return (
17
<main>
18
<h1 className=" text-5xl text-center mt-4">
19
Next JS Cloudinary Integration
20
</h1>
21
22
<CloudinaryUploader />
23
24
<div
25
className="grid grid-cols-1 sm:grid-cols-2
26
md:grid-cols-3 lg:grid-cols-4 gap-4"
27
>
28
{images.total_count > 0 &&
29
images.resources.map((image) => (
30
<div
31
key={image.asset_id}
32
className="container mx-auto max-w-screen-xl px-8 "
33
>
34
<Image
35
className="flex flex-wrap justify-center"
36
src={image.secure_url}
37
height={image.height}
38
width={image.width}
39
alt="My cloudinary image"
40
priority
41
/>
42
</div>
43
))}
44
</div>
45
</main>
46
);
47
}
48
49
export default Home;

I will also add an h2 with the text Uploaded Image if there’s only one uploaded image and Uploaded Images if there is more than one uploaded image

1
import CloudinaryUploader from '@/components/CloudinaryUploader';
2
import { v2 as cloudinary } from 'cloudinary';
3
import Image from 'next/image';
4
5
cloudinary.config({
6
cloud_name: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,
7
api_key: process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY,
8
api_secret: process.env.NEXT_PUBLIC_CLOUDINARY_API_SECRET,
9
});
10
11
async function Home() {
12
const images = await cloudinary.search
13
.expression('next-cloudinary')
14
.execute();
15
16
return (
17
<main>
18
<h1 className=" text-5xl text-center mt-4">
19
Next JS Cloudinary Integration
20
</h1>
21
22
<CloudinaryUploader />
23
24
<h2 className="text-3xl text-center mt-10 mb-2">
25
{images.total_count === 1 ? 'Uploaded Image' : 'Uploaded Images'}
26
</h2>
27
28
<div
29
className="grid grid-cols-1 sm:grid-cols-2
30
md:grid-cols-3 lg:grid-cols-4 gap-4"
31
>
32
{images.total_count > 0 &&
33
images.resources.map((image) => (
34
<div
35
key={image.asset_id}
36
className="container mx-auto max-w-screen-xl px-8 "
37
>
38
<Image
39
className="flex flex-wrap justify-center"
40
src={image.secure_url}
41
height={image.height}
42
width={image.width}
43
alt="My cloudinary image"
44
priority
45
/>
46
</div>
47
))}
48
</div>
49
</main>
50
);
51
}
52
53
export default Home;

The images look good now Good looking images

Grab the full code from the project GitHub repo.

Conclusion

Whether you’re building a gallery, portfolio, or any image-intensive application, or you want to handle image upload in any other Next.js application, the combination of Next.js and Cloudinary provides a robust solution for modern web development needs.

That’s why this guide demonstrated how you can efficiently upload images using the Next Cloudinary package. In addition, you learned how to use the Cloudinary search API for displaying images, ensuring they are well-managed and seamlessly integrated into your projects.