How to Set Up Cloudinary Image Upload in NextJS 13

Roshan Paudel
21 Dec 2024

Image uploads are a common requirement for many web applications, and with the release of Next.js 13, it's easier than ever to perform secure image uploads to Cloudinary. In this tutorial, we'll walk through the steps to set up a basic Cloudinary image upload in a Next.js 13 project.


Before we get started, you'll need to create environment variables to store your Cloudinary credentials securely. Create a .env file in your project directory and add the following variables:


# Secret

Remember to replace <YOUR_CLOUD_NAME> with your actual Cloudinary cloud name.

Setting Up Cloudinary Actions

Let's begin by creating an action file in our project. Create a new folder in /src called actions, and within that folder, create a file named upload.action.ts.

// src/actions/upload.action.ts
"use server";
import { v2 as cloudinary } from "cloudinary";

  cloud_name: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUDNAME,
  api_key: process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY,
  api_secret: process.env.CLOUDINARY_API_SECRET,
  secure: true,

Install the cloudinary package using npm: npm install cloudinary.

Now, let's create a function that will generate a signature and timestamp and return it.

// src/actions/upload.action.ts
export const getSignature = async () => {
  const timestamp = Math.round(new Date().getTime() / 1000);

  if (process.env.CLOUDINARY_API_SECRET) {
    const signature = cloudinary.utils.api_sign_request(
      { timestamp, folder: "next" },

    return { timestamp, signature };
  } else {
    throw new Error("CLOUDINARY_API_SECRET is not defined");

Frontend Implementation

Now, let's implement the frontend part of the image upload.

// app/page.tsx
"use client";
import React from "react";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { getSignature } from "@/actions/upload.action";

Set up the form with a file input:

// pages/upload.tsx
const page = () => {
  return (
    <form action={handleUpload} className="mt-3 w-4/6 mx-auto flex gap-2">
      <Input type="file" name="file" accept="image/*" />

export default page;

Define the handleUpload function to handle the file upload:

// app/page.tsx
const handleUpload = async (formData: FormData) => {
  const files = formData.getAll("file");
  if (files[0].name !== "") {
    const { signature, timestamp } = await getSignature();
    if (process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY) {
      formData.append("api_key", process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY);
      formData.append("signature", signature);
      formData.append("timestamp", timestamp.toString());
      formData.append("folder", "next");
    } else {
      throw new Error("Couldn't find Cloudinary API key");
    const endpoint = process.env.NEXT_PUBLIC_CLOUDINARY_UPLOAD_URL;
    if (endpoint) {
      const data = await fetch(endpoint, {
        method: "POST",
        body: formData,
      }).then((res) => res.json());
    } else {
      throw new Error("Endpoint not defined.");
  } else {
    throw new Error("No files were uploaded.");

In this function, we check if a file was selected, generate a signature and timestamp, append necessary data to the form, and send the payload to the Cloudinary API.

That's it! You've successfully set up a basic Cloudinary image upload in Next.js 13. You can now expand upon this foundation to build more advanced image upload features in your Next.js applications.

Share this article in social media

© 2024 Roshan Paudel