> ## Documentation Index
> Fetch the complete documentation index at: https://cloudinary.com/documentation/llms.txt
> Use this file to discover all available pages before exploring further.

# Upload assets with Server Actions in a Next.js app (video tutorial)

[githublink]: https://github.com/cloudinary-community/cloudinary-examples/tree/main/examples/nextjs-server-actions-upload

## Overview

Add asset upload capabilities to your Next.js application using Server Actions. 

## Video tutorial

  This video is brought to you by Cloudinary's video player - embed your own!Use the controls to set the playback speed, navigate to chapters of interest and select subtitles in your preferred language.

> **TIP**: :title=View the code

You can find the code from this tutorial in [GitHub][githublink].
## Tutorial contents
This tutorial presents the following topics. Click a timestamp to jump to that part of the video.
### Introduction to Server Actions
{table:class=tutorial-bullets}|  | 
| --- | --- |
|{videotime:id=media :min=0 :sec=00 :player=cld} | Server Actions allow you to add server-side capabilities to your Next.js or React app. In this tutorial you'll learn how to upload files to Cloudinary inside a Server Action.

### Using Server Components
{table:class=tutorial-bullets}|  | 
| --- | --- |
|{videotime:id=media :min=0 :sec=12 :player=cld} | Server Actions are built on top of React Actions and give you server-side capabilities inside of a component. Use Server Components to make your app more performant and leverage other benefits. 
|

### Create a Server Action
{table:class=tutorial-bullets}|  | 
| --- | --- |
|{videotime:id=media :min=1 :sec=21 :player=cld} | In your Next.js app, create a new async function to be your Server Action, and pass the form data to the function to get the image to upload.  Note that `image` is the name assigned to the form input.
|

```js
async function create(formData: FormData) {
  'use server';
  const file = formData.get('image');
}
```

### Convert the image to a buffer
{table:class=tutorial-bullets}|  | 
| --- | --- |
|{videotime:id=media :min=2 :sec=50 :player=cld} | To upload the image to Cloudinary you need to turn the file into an array of bytes using an `ArrayBuffer`.
|

```js
async function create(formData: FormData) {
  'use server';
  const file = formData.get('image') as File;
  const arrayBuffer = await file.arrayBuffer();
  const buffer = new Uint8Array(arrayBuffer);
}
```

### Upload the image to Cloudinary
{table:class=tutorial-bullets}|  | 
| --- | --- |
|{videotime:id=media :min=3 :sec=22 :player=cld} | To upload the image to Cloudinary, use the [Node.js SDK](node_integration). [Install and configure](node_configuration_tutorial) the Node.js SDK, then use the [upload_stream](node_image_and_video_upload#node_js_upload_stream) method, wrapped in a promise, to upload the image.
|

```js
async function create(formData: FormData) {
  'use server';
  const file = formData.get('image') as File;
  const arrayBuffer = await file.arrayBuffer();
  const buffer = new Uint8Array(arrayBuffer);
  await new Promise ((resolve, reject) => {
    cloudinary.uploader.upload_stream({}, function (error, result){
      if ( error ) {
        reject(error);
        return;
      }
      resolve(result);
    }).end(buffer);
  })
}
```

### Display the uploaded images
{table:class=tutorial-bullets}|  | 
| --- | --- |
|{videotime:id=media :min=4 :sec=43 :player=cld} | The images that are displayed on the site are selected based on a tag. Ensure that the uploaded files have that same tag by adding it to the upload call. Then make sure to invalidate and refresh the page, using `revalidatePath` so the new images are displayed.
|

```js
import { revalidatePath } from 'next/cache';

...

async function create(formData: FormData) {
  'use server';
  const file = formData.get('image') as File;
  const arrayBuffer = await file.arrayBuffer();
  const buffer = new Uint8Array(arrayBuffer);
  await new Promise ((resolve, reject) => {
    cloudinary.uploader.upload_stream({tags: ['nextjs-server-actions-upload-sneakers']}, function (error, result){
      if ( error ) {
        reject(error);
        return;
      }
      resolve(result);
    }).end(buffer);
  })
  revalidatePath('/');
}
```

## Keep learning

> **READING**:
>
> * Find all upload options in the [Upload API reference](image_upload_api_reference).

> * See what's possible with the [Cloudinary Next.js SDK](nextjs_integration).

> * Watch more [Dev Hints videos](https://www.youtube.com/playlist?list=PL8dVGjLA2oMpaTbvoKCaRNBMQzBUIv7N8) on the [Cloudinary YouTube channel](https://www.youtube.com/cloudinary).

#### If you like this, you might also like...

  
  
  
    Upload Videos in Node.js
    Upload videos to Cloudinary using the Node.js SDK 
  

  
  
  
    List Images in Next.js
    List images using the Next.js app router and Node.js SDK 
  

  
  
  
    AI Generative Fill using Next.js
    Extend images using generative AI in a Next.js app 
  

&nbsp;

&nbsp;Check out the Cloudinary Academy for free self-paced Cloudinary courses on a variety of developer or DAM topics, or register for formal instructor-led courses, either virtual or on-site.
&nbsp;
