# Uploading and Displaying Videos with Next.js

Source: https://tpiros.dev/blog/uploading-and-displaying-videos-with-nextjs

Next.js is a popular open-source React-based frontend framework created and maintained by Vercel. It's a big name in the Jamstack world thanks to its server-side rendering and static site generation capabilities.

Let's build a project that uploads videos using Next.js and Vercel's serverless functions.

# Demo

Check out the working demo in the Codesandbox below.

> If you fork the sample, you'll need to swap the API keys under `pages/api/upload.js` with your own Cloudinary account details.

> Codesandbox has file upload size limits, so you can only upload a small video there. [Grab this one if you don't have a small file handy.](https://res.cloudinary.com/tamas-demo/video/upload/v1621674377/sm_vid_nextjs.mp4) Running the project locally removes that restriction.

<iframe src="https://codesandbox.io/embed/brave-moon-9o66b?fontsize=14&hidenavigation=1&theme=dark&view=preview"
     style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
     title="brave-moon-9o66b"
     allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
     sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
   ></iframe>

> Click `Open Sandbox` above to see the full code.

# Starting with the project

Beyond Next.js, the project uses [TailwindCSS](https://tailwindcss.com), [Formidable](https://www.npmjs.com/package/formidable), and [Cloudinary](https://cloudinary.com). After initialising the project, set up those dependencies:

```
npx create-next-app myApp --use-npm
npm i tailwindcss@latest postcss@latest autoprefixer@latest formidable cloudinary
```

> For the full TailwindCSS setup in a Next.js project, [follow this tutorial](https://tailwindcss.com/docs/guides/nextjs).

Once everything's in place, we can build the serverless function.

# Vercel Serverless Function

Serverless functions are a natural fit for Jamstack apps. Since there's no server-side environment available, you offload that work to serverless functions. For this project, we need one that takes a video from the frontend and uploads it to Cloudinary.

## Upload to Cloudinary

Cloudinary has a [Node.js SDK](https://cloudinary.com/documentation/node_integration) we can use for the upload. But we also need to receive the video file. We'll look at the frontend in a moment. In the serverless function, three things need to happen:

- Override Vercel's default `bodyParser` behaviour
- Add `formidable` to access uploaded files
- Upload the file to Cloudinary

Here's `pages/api/upload.js`:

```javascript

cloudinary.config({
  cloud_name: 'CLOUDINARY-USER-NAME',
  api_key: 'CLOUDINARY-API-KEY',
  api_secret: 'CLOUDINARY-API-SECRET',
});

  api: {
    bodyParser: false,
  },
};

  const data = await new Promise((resolve, reject) => {
    const form = new IncomingForm();

    form.parse(req, (err, fields, files) => {
      if (err) return reject(err);
      resolve({ fields, files });
    });
  });

  const file = data?.files?.inputFile.path;

  const response = await cloudinary.v2.uploader.upload(file, {
    resource_type: 'video',
    public_id: 'my_video',
  });
  return res.json(response);
};
```

Notice `bodyParser: false` is set, form data is parsed by `new IncomingForm()`, and the video gets uploaded via `cloudinary.v2.uploader.upload()`.

> You can test the serverless function with a tool like Postman or Insomnia by running `vercel dev` from the CLI. When sending a video through those tools, use the `inputFile` key, since that's what the code references: `const file = data?.files?.inputFile.path;`.

# The Frontend

With the serverless function sorted, let's build the frontend. Modify `index.js` and drop in the Upload component:

```javascript

// ...
;
// ...
```

## The components

I've split the functionality into 3 components. Since we're in React territory, there are plenty of ways to structure this. Feel free to rearrange things to match how you like to write React.

### Upload.js

The upload component shows a styled file selector (via TailwindCSS). Since we're uploading a file, we need an `onChange` handler on the form. Here's the simplified version:

```javascript

  const onChange = async (event) => {
    // handle the upload
  }

  return (<input type="file" onChange={onChange} />)
}
```

What goes inside the event handler? Since it's a file upload, we reach for the [`FormData` WebAPI](https://developer.mozilla.org/en-US/docs/Web/API/FormData):

```javascript
const onChange = async (event) => {
  event.preventDefault();
  const formData = new FormData();
  const file = event.target.files[0];
  formData.append('inputFile', file);
};
```

Once we've got the `formData`, fire it off to the serverless function:

```javascript
try {
  const response = await fetch('/api/upload', {
    method: 'POST',
    body: formData,
  });
  const data = await response.json();
} catch (error) {
  //
} finally {
  //
}
```

Notice the `try-catch-finally` block. It doesn't get enough love.

### Video.js and Spinner.js

The other two components are cosmetic. `Video.js` displays and plays the video after upload. `Spinner.js` shows a loading indicator while the upload's in progress.

Both components toggle visibility based on state managed through React's `useState` and `useEffect`. [Check the Codesandbox code for the full picture](https://codesandbox.io/s/brave-moon-9o66b).

# Wrap up

This was a fun little project to put together. You could take it in all sorts of directions: transform the video with Cloudinary, call additional APIs from the serverless function (maybe transcribe the video and send the text to a translation service). Hope you picked up something new.
