Next.js image and video upload
Last updated: Feb-26-2026
The Next.js SDK provides React components that make it easy to add file upload functionality to your Next.js application. These components wrap the Cloudinary Upload Widget, giving you a ready-made, responsive UI that supports uploads from multiple sources including local files, camera, URL, and more.
You can implement uploads using either unsigned upload presets for quick setup, or signed uploads with server-side signature generation for enhanced security.
Upload options
The Next.js SDK offers two components for uploading assets. Both components open the Cloudinary Upload Widget, a ready-made, responsive user interface that enables your users to upload files from a variety of sources (local files, camera, URL, and more).
- CldUploadButton - A simple button component that opens the upload widget when clicked. Best for straightforward upload functionality.
- CldUploadWidget - A flexible component that provides full programmatic control over the upload widget, including the ability to open it from custom UI elements.
Both components provide access to callback functions, configuration options, and instance methods to customize the upload experience for your users.
Using CldUploadButton
The CldUploadButton component provides the simplest way to add upload functionality to your Next.js app. It renders a button that, when clicked, opens the Cloudinary Upload Widget.
Basic usage
This example shows the minimal setup required to add an upload button to your Next.js application. The uploadPreset prop specifies which upload preset to use, which defines the upload configuration:
Handling upload results
Use the onSuccess callback to handle successful uploads:
Customizing the button
The CldUploadButton component inherits all props from CldUploadWidget (including callback functions like onSuccess, onQueuesEnd, etc.), however, it handles children differently.
Button-specific props:
-
className- CSS class name for styling -
onClick- Click event handler function
All other HTML button props (like style, disabled, type, etc.) are spread onto the <button> element, giving you full control over the button's appearance and behavior.
Button content:
You can customize the content inside the button by placing elements between the opening and closing <CldUploadButton> tags. If no content is provided, the button displays "Upload" by default.
CldUploadWidget, the content between the tags is a render function that receives widget state. In CldUploadButton, the content is simply the button's label or inner elements.Example with custom styling:
Create styling for your button in CSS, for example, in globals.css:
Set the className prop to the name of the class, in this case, custom-button:
This renders a styled button with the text "Upload Files":
Example with default label:
If you don't provide content between the tags, the button displays "Upload":
This renders a styled button with the text "Upload":
Example with onClick handler:
You can add custom logic when the button is clicked, before the upload widget opens:
Using CldUploadWidget
The CldUploadWidget component provides more control over the upload experience, allowing you to customize when and how the widget opens.
CldUploadWidget won't render any UI by default. It uses a render prop pattern where you provide a function that returns your custom UI, giving you complete control over the trigger element and interface.Basic usage
Render function parameters
The render function receives an object with the following parameters:
| Parameter | Type | Description |
|---|---|---|
cloudinary |
Cloudinary | The Cloudinary instance which creates and manages the Widget instance |
error |
string | The error, if any, produced by an upload widget action |
isLoading |
boolean | Designates whether the upload widget is loading and initializing |
results |
object | The event that triggered the results and information related to that event, which can include upload results |
widget |
Widget | The widget instance attached to the current component |
open |
function | Instance method to open the widget |
close |
function | Instance method to close the widget |
hide |
function | Instance method to hide the widget |
show |
function | Instance method to show the widget |
minimize |
function | Instance method to minimize the widget |
destroy |
function | Instance method to destroy the widget |
isDestroyed |
function | Instance method to check if widget is destroyed |
isShowing |
function | Instance method to check if widget is showing |
isMinimized |
function | Instance method to check if widget is minimized |
update |
function | Instance method to update widget options |
Example accessing multiple parameters:
Handling upload events
The widget provides callback functions for different events in the upload lifecycle. There are two types of callbacks:
- Callback Functions - Receive both results and options (including widget instance and instance methods)
- Action Callbacks - Receive only results (designed for Server Actions workflow)
Callback Functions
These callbacks receive (results, options) or (error, options) parameters:
| Callback | Description |
|---|---|
onAbort |
Triggered when upload is aborted |
onBatchCancelled |
Triggered when batch upload is cancelled |
onClose |
Triggered when widget is closed |
onDisplayChanged |
Triggered when widget display changes |
onError |
Triggered when an error occurs |
onOpen |
Triggered when widget opens (receives only widget parameter) |
onPublicId |
Triggered when public ID is generated |
onQueuesEnd |
Triggered when all uploads in queue complete |
onQueuesStart |
Triggered when upload queue starts |
onRetry |
Triggered when upload is retried |
onShowCompleted |
Triggered when completed uploads are shown |
onSourceChanged |
Triggered when upload source changes |
onSuccess |
Triggered on successful upload |
onTags |
Triggered when tags are processed |
onUpload |
(Deprecated) Use onSuccess instead |
onUploadAdded |
Triggered when file is added to upload queue |
Example:
This example demonstrates using multiple callback functions to handle different stages of the upload process. It uses onSuccess to capture upload results, onQueuesEnd to automatically close the widget after all uploads complete, and onError to handle any upload failures:
Action Callbacks
These callbacks receive only the (results) parameter for use with Server Actions:
| Action Callback | Description |
|---|---|
onAbortAction |
Action version of onAbort |
onBatchCancelledAction |
Action version of onBatchCancelled |
onCloseAction |
Action version of onClose |
onDisplayChangedAction |
Action version of onDisplayChanged |
onPublicIdAction |
Action version of onPublicId |
onQueuesEndAction |
Action version of onQueuesEnd |
onQueuesStartAction |
Action version of onQueuesStart |
onRetryAction |
Action version of onRetry |
onShowCompletedAction |
Action version of onShowCompleted |
onSourceChangedAction |
Action version of onSourceChanged |
onSuccessAction |
Action version of onSuccess |
onTagsAction |
Action version of onTags |
onUploadAddedAction |
Action version of onUploadAdded |
Callback parameters
Most callbacks provide parameters with the following structure:
For Callback Functions:
-
results(object) - Containsevent(string) andinfo(object with upload details likepublic_id,secure_url,width,height, etc.) -
options(object) - Containswidget(Widget instance) and instance methods
For Action Callbacks:
-
results(object) - Containseventandinfoonly
Example results object:
Instance methods
The Upload Widget exposes instance methods that provide greater control over the upload experience. These are available through the children function parameters or callback options:
| Method | Description |
|---|---|
open() |
Renders an existing widget currently in memory, but not currently displayed |
close() |
Closes and resets the widget to its initial state without removing it from memory |
hide() |
Hides a previously rendered widget while retaining its current state in memory |
show() |
Renders a previously hidden widget |
minimize() |
Minimizes the widget |
destroy() |
Closes the widget and completely removes it from the DOM. Returns a promise that resolves upon cleanup completion |
isShowing() |
Returns whether the widget is currently visible |
isMinimized() |
Returns whether the widget is currently minimized |
isDestroyed() |
Returns whether the destroy method was called on this instance |
update(options) |
Updates a widget currently in memory with new options |
Example using instance methods:
This example demonstrates how to programmatically control the upload widget using instance methods. It creates multiple buttons that allow you to open, close, hide, show, and minimize the widget, as well as check its current state:
Signed vs unsigned uploads
There are two options when using the upload components: signed and unsigned. These options allow you to control the amount of security and restrictions you place on uploads.
Unsigned uploads:
- Simpler to implement (no server-side code required)
- Require an unsigned upload preset
- Anyone with the preset name can upload
- Best for: Public assets, demos, prototypes
Signed uploads:
- More secure (requires server-side signature generation)
- Prevents unauthorized uploads
- Allows more control over upload parameters
- Best for: Production applications, restricted uploads, sensitive content
Using unsigned uploads
Unsigned uploads are the simplest option and work well for public assets. All the examples in Using CldUploadButton and Using CldUploadWidget show unsigned uploads.
For unsigned uploads, you need to set the uploadPreset prop to your unsigned upload preset.
For CldUploadButton:
For CldUploadWidget:
Using signed uploads
Signed upload requests provide enhanced security for your file uploads. This helps prevent unauthorized uploads to your Cloudinary account.
For signed uploads, you need to set the signatureEndpoint prop to an API endpoint that'll provide the signature.
For CldUploadButton:
For CldUploadWidget:
You can optionally also provide a signed upload preset in the uploadPreset prop, but this isn't required for signed uploads.
For CldUploadButton:
For CldUploadWidget:
Creating the signature endpoint
When working in Next.js, you can use the Cloudinary Node SDK in a server-side API route to sign your upload requests.
Step 1: Install the Cloudinary Node SDK
Step 2: Set up environment variables
Add your Cloudinary API credentials to your environment variables:
CLOUDINARY_API_SECRET to the client. Keep it in server-side environment variables only (without the NEXT_PUBLIC_ prefix).Step 3: Create the API endpoint
The implementation differs based on whether you're using the App Router or Pages Router.
For App Router (Next.js 13+):
Create a new file at app/api/sign-cloudinary-params/route.js:
For Pages Router:
Create a new file at pages/api/sign-cloudinary-params.js:
That's it! The upload components will now automatically call your signature endpoint before each upload to generate a secure signature.
Configuration
Both CldUploadButton and CldUploadWidget support configuration through props.
Cloudinary environment configuration
The upload components automatically use environment variables for Cloudinary configuration:
-
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME- Your Cloudinary cloud name (required) -
NEXT_PUBLIC_CLOUDINARY_API_KEY- Your API key (optional, for signed uploads) -
CLOUDINARY_API_SECRET- Your API key (optional, for signed uploads)
You can also use the config prop to configure Cloudinary settings:
config prop. If you have NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME set, it will override any cloudName specified in the config prop. To use the config prop values, you must remove or unset the corresponding environment variables.Upload Widget configuration options
You can pass any Upload Widget configuration option using the options prop:
Common configuration options include:
| Option | Type | Description |
|---|---|---|
sources |
array | Upload sources (e.g., ['local', 'camera', 'url']) |
multiple |
boolean | Allow multiple file uploads |
maxFiles |
number | Maximum number of files |
maxFileSize |
number | Maximum file size in bytes |
clientAllowedFormats |
array | Allowed file formats (e.g., ['jpg', 'png']) |
resourceType |
string | Resource type ('image', 'video', 'raw', 'auto') |
folder |
string | Upload folder path |
tags |
array | Tags to apply to uploaded assets |
context |
object | Contextual metadata |
styles |
object | Custom widget styling |
text |
object | Custom widget text/labels |
language |
string | Widget language (e.g., 'en', 'es') |
showAdvancedOptions |
boolean | Show advanced upload options |
showCompletedButton |
boolean | Show completed button |
showUploadMoreButton |
boolean | Show upload more button |
showSkipCropButton |
boolean | Show skip crop button |
cropping |
boolean | Enable image cropping |
croppingAspectRatio |
number | Aspect ratio for cropping |
croppingDefaultSelectionRatio |
number | Default selection ratio |
croppingShowDimensions |
boolean | Show dimensions during cropping |
croppingCoordinatesMode |
string | Coordinates mode ('custom' or 'face') |
croppingShowBackButton |
boolean | Show back button in cropping view |
Example with multiple configuration options:
- Learn about the Next.js SDK and its components.
- See examples of image transformations and video transformations using Next.js code.
- Check out the Upload Widget Reference for complete upload widget documentation.
- Learn about configuring Upload Presets for your uploads.
- Understand signed upload requests for secure uploads.