{"id":28096,"date":"2021-06-02T08:54:57","date_gmt":"2021-06-02T08:54:57","guid":{"rendered":"http:\/\/How-To-Convert-Video-to-GIF-in-Next.js-With-Cloudinary"},"modified":"2025-03-08T13:42:47","modified_gmt":"2025-03-08T21:42:47","slug":"how-to-convert-video-to-gif-in-next-js-with-cloudinary","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/","title":{"rendered":"How To Convert Video to GIF in Next.js"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>In this media jam, we will discuss how to build a Video to GIF converter in Next.js using Cloudinary. We will use Next.js API routes to upload the video to Cloudinary and then fetch the converted GIF. We will also see how to build a download button to download the GIF and a clear button to delete the video from Cloudinary.<\/p>\n<p>If you want to jump right into the code, check out the <a href=\"https:\/\/github.com\/lelouchB\/video-2-gif\">GitHub Repo here<\/a>.<\/p>\n<h2>CodeSandbox<\/h2>\n<\/div>\n\n\n  <div class=\"wp-block-cloudinary-code-sandbox \">\n    <iframe\n      src=\"https:\/\/codesandbox.io\/embed\/video-2-gif-71y2y?theme=dark&amp;codemirror=1&amp;highlights=&amp;editorsize=50&amp;fontsize=14&amp;expanddevtools=0&amp;hidedevtools=0&amp;eslint=0&amp;forcerefresh=0&amp;hidenavigation=0&amp;initialpath=%2F&amp;module=%2Fpages%2Findex.js&amp;moduleview=0&amp;previewwindow=&amp;view=&amp;runonclick=1\"\n      height=\"500\"\n      style=\"width: 100%;\"\n      title=\"Video-2-GIF\"\n      loading=\"lazy\"\n      allow=\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\"\n      sandbox=\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\"\n    ><\/iframe>\n  <\/div>\n\n\n<div class=\"wp-block-cloudinary-markdown \"><h2>Setup<\/h2>\n<p>You will create the initial Next.js app using the <code>create-next-app<\/code> template. Run the following commands in your terminal.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">npx create-next-app video-2-gif\ncd video-2-gif\nnpm run dev\n<\/code><\/span><\/pre>\n<p>The last command, <code>npm run dev<\/code>, will start the development server on <a href=\"http:\/\/localhost:3000\/\">http:\/\/localhost:3000\/<\/a>. You can stop the development server by hitting <strong>CTRL+C<\/strong> in the terminal.<\/p>\n<p>You will use <a href=\"https:\/\/www.npmjs.com\/package\/cloudinary\">Cloudinary node.js server-side SDK<\/a> in the API routes to upload and delete the video from Cloudinary. You will also need to install <code>cloudinary-react<\/code> to display GIF to the users, <code>react-dropzone<\/code> for drag &amp; drop operation, and <code>axios<\/code> to make the requests to the API routes from the frontend.<\/p>\n<p>Run the following command to install the required libraries.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">npm install cloudinary cloudinary-react react-dropzone axios\n<\/code><\/span><\/pre>\n<p>To store your Cloudinary API Keys securely, create a <code>.env<\/code> file in your root directory by running the following command.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">touch<\/span> <span class=\"hljs-selector-class\">.env<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Head over to your Cloudinary dashboard and copy the API Keys.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/jesse-thisdot\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1621692370\/e-603fc45fe6c0b4006873802f\/edeyktay5kzqbnoqkdy2.png\" alt=\"Cloudinary API Keys\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1330\" height=\"385\"\/><\/p>\n<p>Paste the API keys in the <code>.env<\/code> file<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">NEXT_PUBLIC_CLOUDINARY_CLOUD=<span class=\"hljs-string\">''<\/span>\nCLOUDINARY_API_KEY=<span class=\"hljs-string\">''<\/span>\nCLOUDINARY_API_SECRET=<span class=\"hljs-string\">''<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>In this tutorial, we will not discuss the styling of the app. You can copy-paste the CSS used to style the app in the <code>styles\/Home.module.css<\/code> file from <a href=\"https:\/\/raw.githubusercontent.com\/lelouchB\/video-2-gif\/main\/styles\/Home.module.css\">here<\/a>.<\/p>\n<h2>How to Upload Videos to Cloudinary<\/h2>\n<p>In this section, we will discuss how to upload the videos to Cloudinary using the <code>react-dropzone<\/code> library and <code>FileReader<\/code> API.<\/p>\n<p>The first step is to create a dropzone in your app that only accepts videos and then read the selected video using the <code>FileReader<\/code> API.<\/p>\n<p>Update the <code>pages\/index.js<\/code> file with the following code.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> React, { useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> Head <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/head\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { useDropzone } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react-dropzone\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> styles <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/styles\/Home.module.css\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> axios <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"axios\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { Transformation, Image } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"cloudinary-react\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Home<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> &#91;uploadVideo, setUploadVideo] = useState(<span class=\"hljs-string\">\"\"<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> &#91;publicId, setPublicId] = useState(<span class=\"hljs-string\">\"\"<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> &#91;fileName, setFileName] = useState(<span class=\"hljs-string\">\"\"<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> &#91;progress, setProgress] = useState(<span class=\"hljs-string\">\"\"<\/span>);\n\n  <span class=\"hljs-keyword\">const<\/span> { getRootProps, getInputProps } = useDropzone({\n    <span class=\"hljs-attr\">accept<\/span>: <span class=\"hljs-string\">\"video\/*\"<\/span>,\n    <span class=\"hljs-attr\">multiple<\/span>: <span class=\"hljs-literal\">false<\/span>,\n    <span class=\"hljs-attr\">onDrop<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">acceptedFiles<\/span>) =&gt;<\/span> {\n      <span class=\"hljs-keyword\">const<\/span> file = acceptedFiles&#91;<span class=\"hljs-number\">0<\/span>];\n      setFileName(file.name);\n\n      <span class=\"hljs-keyword\">const<\/span> reader = <span class=\"hljs-keyword\">new<\/span> FileReader();\n      reader.readAsDataURL(file);\n      reader.onload = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n        setUploadVideo(reader.result);\n      };\n\n      reader.onerror = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n        <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\">\"Error has Occured\"<\/span>);\n      };\n    },\n  });\n\n  <span class=\"hljs-keyword\">const<\/span> convert = <span class=\"hljs-keyword\">async<\/span> () =&gt; {\n    setProgress(<span class=\"hljs-string\">\"Converting Video to GIF\"<\/span>);\n    <span class=\"hljs-built_in\">console<\/span>.log(uploadVideo)\n  };\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.container}<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Head<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">title<\/span>&gt;<\/span>Video 2 GIF Converter<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">title<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">link<\/span> <span class=\"hljs-attr\">rel<\/span>=<span class=\"hljs-string\">\"icon\"<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"\/favicon.ico\"<\/span> \/&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Head<\/span>&gt;<\/span>\n\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.main}<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.title}<\/span>&gt;<\/span>Video 2 GIF Converter<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.card}<\/span> {<span class=\"hljs-attr\">...getRootProps<\/span>()}&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> {<span class=\"hljs-attr\">...getInputProps<\/span>()} \/&gt;<\/span>\n          {fileName ? (\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n              <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Selected Video: {fileName}.<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>{progress}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          ) : (\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Drag 'n' drop some files here, or click to select files<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n          )}\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.grid}<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.btn}<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{convert}<\/span>&gt;<\/span>Convert<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  );\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>In the above code, you start by importing all the packages and then initialize four different states using the <code>useState()<\/code> hook.<\/p>\n<ul>\n<li>\n<code>uploadVideo<\/code> &#8211; for video data URL.<\/li>\n<li>\n<code>publicId<\/code> &#8211; for the <code>public_id<\/code> of video, sent after a successful upload.<\/li>\n<li>\n<code>fileName<\/code> &#8211; for storing the original name of the video.<\/li>\n<li>\n<code>progress<\/code> &#8211; for showing users the current progress of requests.<\/li>\n<\/ul>\n<p>You then define the <code>useDropzone()<\/code> hook with configuration to accept only video files one at a time. In the <code>onDrop()<\/code> parameter of <code>useDropzone()<\/code> hook, you first set the <code>fileName<\/code> state by passing the name of the file to <code>setFileName()<\/code> method and then using <code>FileReader<\/code> API\u2019s <code>readAsDataURL()<\/code> and <code>onload()<\/code> method, you store the data URL of the video to <code>uploadVideo<\/code> state. You can read more about <code>FileReader<\/code> API <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/FileReader\">here<\/a>.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">onDrop: <span class=\"hljs-function\">(<span class=\"hljs-params\">acceptedFiles<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> file = acceptedFiles&#91;<span class=\"hljs-number\">0<\/span>];\n  setFileName(file.name);\n\n  <span class=\"hljs-keyword\">const<\/span> reader = <span class=\"hljs-keyword\">new<\/span> FileReader();\n  reader.readAsDataURL(file);\n  reader.onload = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    setUploadVideo(reader.result);\n  };\n\n  reader.onerror = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\">\"Error has Occured\"<\/span>);\n  };\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>You also create a function named <code>convert<\/code>, which is triggered when the <code>Convert<\/code> button is clicked. Currently, it logs the <code>uploadVideo<\/code> state or the video data URL to the console, as seen below.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/jesse-thisdot\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1621692734\/e-603fc45fe6c0b4006873802f\/qecz9ybmgzzcvz21u9qr.png\" alt=\"Video Data URL\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1910\" height=\"860\"\/><\/p>\n<p>Now that you have converted the video to a data URL, the next step is to send the data URL to the API route to be uploaded to Cloudinary. For this, you will create a file named <code>uploadVideo.js<\/code> in the <code>pages\/api<\/code> folder. Run the following command to create it.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">touch pages\/api\/uploadVideo.js\n<\/code><\/span><\/pre>\n<p>Add the following code to <code>uploadVideo.js<\/code>.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ pages\/api\/uploadVideo.js<\/span>\n<span class=\"hljs-keyword\">var<\/span> cloudinary = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">\"cloudinary\"<\/span>).v2;\n\ncloudinary.config({\n  <span class=\"hljs-attr\">cloud_name<\/span>: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD,\n  <span class=\"hljs-attr\">api_key<\/span>: process.env.CLOUDINARY_API_KEY,\n  <span class=\"hljs-attr\">api_secret<\/span>: process.env.CLOUDINARY_API_SECRET,\n});\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> config = {\n  <span class=\"hljs-attr\">api<\/span>: {\n    <span class=\"hljs-attr\">bodyParser<\/span>: {\n      <span class=\"hljs-attr\">sizeLimit<\/span>: <span class=\"hljs-string\">\"10mb\"<\/span>,\n    },\n  },\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-keyword\">async<\/span> (req, res) =&gt; {\n  <span class=\"hljs-keyword\">const<\/span> videoDataUrl = req.body.videoDataUrl;\n\n  <span class=\"hljs-keyword\">let<\/span> publicId = <span class=\"hljs-string\">\"\"<\/span>;\n\n  <span class=\"hljs-keyword\">await<\/span> cloudinary.uploader.upload(\n    videoDataUrl,\n    { <span class=\"hljs-attr\">resource_type<\/span>: <span class=\"hljs-string\">\"video\"<\/span>, <span class=\"hljs-attr\">video_codec<\/span>: <span class=\"hljs-string\">\"auto\"<\/span> },\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">error, result<\/span>) <\/span>{\n      <span class=\"hljs-keyword\">if<\/span> (result) {\n        publicId = result.public_id;\n        res.status(<span class=\"hljs-number\">200<\/span>).json(publicId);\n      }\n      <span class=\"hljs-keyword\">if<\/span> (error) {\n        <span class=\"hljs-built_in\">console<\/span>.error(error);\n        res.status(<span class=\"hljs-number\">400<\/span>).json(error);\n      }\n    }\n  );\n};\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>In the above code, you create an instance of <code>cloudinary<\/code> and pass the API keys stored in the <code>.env<\/code> file. You also limit the incoming request to ten MB, i.e., you can only upload videos less than ten MB. You can change this parameter according to your needs.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> config = {\n  <span class=\"hljs-attr\">api<\/span>: {\n    <span class=\"hljs-attr\">bodyParser<\/span>: {\n      <span class=\"hljs-attr\">sizeLimit<\/span>: <span class=\"hljs-string\">\"10mb\"<\/span>,\n    },\n  },\n};\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>You export an async function that extracts the data URL of the video from the request body and then use <a href=\"https:\/\/cloudinary.com\/documentation\/image_upload_api_reference\">Cloudinary\u2019s upload API<\/a> to upload the video to your Cloudinary account.<\/p>\n<p>In the parameters of the <code>uploader.upload()<\/code> method, you specify the <code>resource_type<\/code> of the upload to <code>video<\/code> and the <code>video_codec<\/code>, to <code>auto<\/code>, which optimizes and reduces the file size. The <code>public_id<\/code> of the video is sent as a response to the frontend if the video is uploaded successfully.<\/p>\n<p>The next step is to update the <code>convert<\/code> function in <code>index.js<\/code> to make the <code>POST<\/code> request to <code>\/uploadVideo<\/code> route with the video\u2019s data URL in the request body. If a successful response is received, you change the <code>progress<\/code> state to <code>&quot;Converted to GIF Successfully&quot;<\/code>.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">const<\/span> convert = <span class=\"hljs-keyword\">async<\/span> () =&gt; {\n  setProgress(<span class=\"hljs-string\">\"Converting Video to GIF\"<\/span>);\n\n  <span class=\"hljs-keyword\">const<\/span> res = <span class=\"hljs-keyword\">await<\/span> axios.post(<span class=\"hljs-string\">\"\/api\/uploadVideo\"<\/span>, {\n    <span class=\"hljs-attr\">videoDataUrl<\/span>: uploadVideo,\n  });\n  <span class=\"hljs-keyword\">const<\/span> data = <span class=\"hljs-keyword\">await<\/span> res.data;\n\n  setPublicId(data);\n  <span class=\"hljs-keyword\">if<\/span> (res.statusText == <span class=\"hljs-string\">\"OK\"<\/span>) {\n    setProgress(<span class=\"hljs-string\">\"Converted to GIF Successfully\"<\/span>);\n  }\n};\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The <code>public_id<\/code> of the video is stored in the <code>publicId<\/code> state using the <code>setPublicId()<\/code> method. Using the <code>publicId<\/code> state, you show users a preview of the GIF made from the video. In the <code>index.js<\/code> file, add the following code after the <code>div<\/code> with <code>className={styles.grid}<\/code>.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">{\n  publicId &amp;&amp; (\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Image<\/span>\n      <span class=\"hljs-attr\">format<\/span>=<span class=\"hljs-string\">\"gif\"<\/span>\n      <span class=\"hljs-attr\">cloudName<\/span>=<span class=\"hljs-string\">{process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD}<\/span>\n      <span class=\"hljs-attr\">publicId<\/span>=<span class=\"hljs-string\">{publicId<\/span> + \"<span class=\"hljs-attr\">.gif<\/span>\"}\n      <span class=\"hljs-attr\">resourceType<\/span>=<span class=\"hljs-string\">\"video\"<\/span>\n      <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"600\"<\/span>\n      <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"auto\"<\/span>\n    &gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Transformation<\/span> <span class=\"hljs-attr\">flags<\/span>=<span class=\"hljs-string\">\"lossy\"<\/span> \/&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Transformation<\/span> <span class=\"hljs-attr\">quality<\/span>=<span class=\"hljs-string\">\"auto:low\"<\/span> \/&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Transformation<\/span> <span class=\"hljs-attr\">effect<\/span>=<span class=\"hljs-string\">\"loop\"<\/span> \/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Image<\/span>&gt;<\/span>\n  )\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>You use the <code>Image<\/code> component from <code>cloudinary-react<\/code> to display the GIF. To convert a video to GIF, you need to add a <code>.gif<\/code> extension to its <code>public_id<\/code> when fetching the GIF or while displaying it as shown in the above code. You also need to pass <code>resourceType=&quot;video&quot;<\/code> in the <code>Image<\/code> component.<\/p>\n<p>Here is the entire video to GIF conversion in action.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/jesse-thisdot\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1621692452\/e-603fc45fe6c0b4006873802f\/k7h3bs3fjqtjpya3zwi7.gif\" alt=\"Converting to GIF\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1280\" height=\"720\"\/><\/p>\n<h2>How To Download the GIF<\/h2>\n<p>You have successfully converted the video to GIF, but what if the user wants to download the GIF on their system. In this section, you will create the download button to download the GIF.<\/p>\n<p>First, create another button named <code>Download<\/code> in the <code>div<\/code> with <code>className={styles.grid}<\/code>.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.grid}<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.btn}<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{convert}<\/span>&gt;<\/span>Convert<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.btn}<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{download}<\/span>&gt;<\/span>Download<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Now, add the following code after the <code>convert<\/code> function to create the <code>download<\/code> function triggered when the <code>Download<\/code> button is clicked.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">const<\/span> download = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n  setProgress(<span class=\"hljs-string\">\"Downloading GIF\"<\/span>);\n\n  axios({\n    <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">\"get\"<\/span>,\n    <span class=\"hljs-attr\">url<\/span>: <span class=\"hljs-string\">`https:\/\/res.cloudinary.com\/<span class=\"hljs-subst\">${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD}<\/span>\/video\/upload\/fl_lossy\/q_auto:low\/<span class=\"hljs-subst\">${publicId}<\/span>.gif`<\/span>,\n    <span class=\"hljs-attr\">responseType<\/span>: <span class=\"hljs-string\">\"blob\"<\/span>,\n  })\n    .then(<span class=\"hljs-function\">(<span class=\"hljs-params\">response<\/span>) =&gt;<\/span> {\n      <span class=\"hljs-keyword\">var<\/span> link = <span class=\"hljs-built_in\">document<\/span>.createElement(<span class=\"hljs-string\">\"a\"<\/span>);\n      link.href = <span class=\"hljs-built_in\">window<\/span>.URL.createObjectURL(response.data);\n      link.download = fileName + <span class=\"hljs-string\">\".gif\"<\/span>;\n\n      <span class=\"hljs-built_in\">document<\/span>.body.appendChild(link);\n\n      link.click();\n      setProgress(<span class=\"hljs-string\">\"GIF Downloaded\"<\/span>);\n\n      setTimeout(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\"><\/span>) <\/span>{\n        <span class=\"hljs-built_in\">window<\/span>.URL.revokeObjectURL(link);\n      }, <span class=\"hljs-number\">200<\/span>);\n    })\n    .catch(<span class=\"hljs-function\">(<span class=\"hljs-params\">error<\/span>) =&gt;<\/span> {\n      <span class=\"hljs-built_in\">console<\/span>.error(error);\n    })\n};\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>In the above code, you start by changing the <code>progress<\/code> state to <code>&quot;Downloading GIF&quot;<\/code> and then make a <code>POST<\/code> request with <code>axios<\/code> to fetch the GIF from Cloudinary as a <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Blob\">Blob object<\/a>.<\/p>\n<p>A <code>blob<\/code> is a file-like object of immutable, raw data, which can be read as text or binary data. This blob is passed to the  <code>URL.createObjectURL()<\/code> method to create a <code>DOMString<\/code> containing a URL representing the <a href=\"https:\/\/cloudinary.com\/glossary\/interlaced-gif\">GIF<\/a>.<\/p>\n<p>This URL is passed to the <code>link<\/code> element, which is nothing but an <code>Anchor<\/code> or an <code>a<\/code> element. When this <code>link<\/code> element is clicked, the resource or the GIF is downloaded on your system. After downloading the file, the URL is released using <code>setTimeout<\/code> function and <code>URL.revokeObjectURL()<\/code> method. You can read more about the <code>URL.createObjectURL<\/code> method <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/URL\/createObjectURL\">here<\/a>.<\/p>\n<p>Here is the download button in action.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/jesse-thisdot\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1621692511\/e-603fc45fe6c0b4006873802f\/h1t6rfpfwwyplihijmzp.gif\" alt=\"Downloading the GIF\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1280\" height=\"720\"\/><\/p>\n<h2>How To Delete the Video<\/h2>\n<p>You also need to delete the video from your Cloudinary account to not run out of storage. In this section, you will create a <code>button<\/code> named <code>Clear<\/code> which will delete the video from your Cloudinary account and reset the states.<\/p>\n<p>Create a button named <code>Clear<\/code> after <code>Convert<\/code> and <code>Download<\/code> buttons.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.grid}<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.btn}<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{convert}<\/span>&gt;<\/span>Convert<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.btn}<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{download}<\/span>&gt;<\/span>Download<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{styles.btn}<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{clear}<\/span>&gt;<\/span>Clear<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Now, create a function named <code>clear<\/code> which makes a <code>POST<\/code> request to <code>deleteVideo<\/code> API route with the <code>public_id<\/code> of the video to be deleted in the request body. This function also clears the four states to their initial value, i.e., empty strings and logs the response from the API route in the console.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">const<\/span> clear = <span class=\"hljs-keyword\">async<\/span> () =&gt; {\n  <span class=\"hljs-keyword\">await<\/span> setFileName(<span class=\"hljs-string\">\"\"<\/span>);\n  <span class=\"hljs-keyword\">await<\/span> setProgress(<span class=\"hljs-string\">\"\"<\/span>);\n  <span class=\"hljs-keyword\">await<\/span> setUploadVideo(<span class=\"hljs-string\">\"\"<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> res = <span class=\"hljs-keyword\">await<\/span> axios.post(<span class=\"hljs-string\">\"\/api\/deleteVideo\"<\/span>, {\n    publicId,\n  });\n  <span class=\"hljs-keyword\">const<\/span> data = <span class=\"hljs-keyword\">await<\/span> res.data;\n  <span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-built_in\">console<\/span>.log(data);\n  <span class=\"hljs-keyword\">await<\/span> setPublicId(<span class=\"hljs-string\">\"\"<\/span>);\n};\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Create the API route <code>\/deleteVideo<\/code> by creating a file named <code>deleteVideo.js<\/code> in <code>pages\/api<\/code> folder. Run the following command to create it.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">touch pages\/api\/deleteVideo.js\n<\/code><\/span><\/pre>\n<p>Add the following code <code>deleteVideo.js<\/code>.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ pages\/api\/deleteVideo.js<\/span>\n<span class=\"hljs-keyword\">var<\/span> cloudinary = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">\"cloudinary\"<\/span>).v2;\n\ncloudinary.config({\n  <span class=\"hljs-attr\">cloud_name<\/span>: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD,\n  <span class=\"hljs-attr\">api_key<\/span>: process.env.CLOUDINARY_API_KEY,\n  <span class=\"hljs-attr\">api_secret<\/span>: process.env.CLOUDINARY_API_SECRET,\n});\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-keyword\">async<\/span> (req, res) =&gt; {\n  <span class=\"hljs-keyword\">const<\/span> publicId = req.body.publicId;\n\n  <span class=\"hljs-keyword\">await<\/span> cloudinary.uploader\n    .destroy(publicId, { <span class=\"hljs-attr\">resource_type<\/span>: <span class=\"hljs-string\">\"video\"<\/span> }, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\">error, result<\/span>) <\/span>{\n      <span class=\"hljs-keyword\">if<\/span> (result) {\n        res.status(<span class=\"hljs-number\">200<\/span>).json(<span class=\"hljs-string\">\"Video Deleted\"<\/span>);\n      }\n    })\n    .catch(<span class=\"hljs-function\">(<span class=\"hljs-params\">e<\/span>) =&gt;<\/span> {\n      <span class=\"hljs-built_in\">console<\/span>.error(e);\n    })\n};\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The above code is similar to the <code>uploadVideo.js<\/code> file, but here you extract the <code>public_id<\/code> of the video from the request body and use the <code>uploader.destroy()<\/code> function to delete the video. You can read more about this method <a href=\"https:\/\/cloudinary.com\/documentation\/image_upload_api_reference#destroy_method\">here<\/a>.<\/p>\n<p>After the successful response from Cloudinary, you send a message <code>&quot;Video Deleted&quot;<\/code> to the frontend.<\/p>\n<p>Here is the <code>Clear<\/code> button deleting the video and resetting the state.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/jesse-thisdot\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1621692590\/e-603fc45fe6c0b4006873802f\/afig8e5o03pg2lhbdtad.gif\" alt=\"Deleting the Video\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1280\" height=\"720\"\/><\/p>\n<h2>Conclusion<\/h2>\n<p>In this media jam, we saw how to build a Video to GIF converter using Next.js and Cloudinary. We also saw how to create a download button to download the GIF and a clear button to delete the video from Cloudinary.<\/p>\n<p>Here are some additional resources that can be helpful:<\/p>\n<ul>\n<li>\n<a href=\"https:\/\/nextjs.org\/docs\">Next.js Docs<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/cloudinary.com\/documentation\/image_upload_api_reference\">Cloudinary Docs<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/react-dropzone.js.org\/\">React Dropzone<\/a>\n<\/li>\n<\/ul>\n<p>Happy coding!<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":28097,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[134,212,371,303],"class_list":["post-28096","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-guest-post","tag-next-js","tag-under-review","tag-video"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>How To Convert Video to GIF in Next.js<\/title>\n<meta name=\"description\" content=\"In this media jam, we will discuss how to build a Video to GIF converter in Next.js using Cloudinary. We will use Next.js API routes to upload the video to Cloudinary and then fetch the converted GIF. We will also see how to build a download button to download the GIF and a clear button to delete the video from Cloudinary.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How To Convert Video to GIF in Next.js\" \/>\n<meta property=\"og:description\" content=\"In this media jam, we will discuss how to build a Video to GIF converter in Next.js using Cloudinary. We will use Next.js API routes to upload the video to Cloudinary and then fetch the converted GIF. We will also see how to build a download button to download the GIF and a clear button to delete the video from Cloudinary.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2021-06-02T08:54:57+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-03-08T21:42:47+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1681925401\/Web_Assets\/blog\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643-png?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"1920\" \/>\n\t<meta property=\"og:image:height\" content=\"909\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"How To Convert Video to GIF in Next.js\",\"datePublished\":\"2021-06-02T08:54:57+00:00\",\"dateModified\":\"2025-03-08T21:42:47+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/\"},\"wordCount\":9,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925401\/Web_Assets\/blog\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643.png?_i=AA\",\"keywords\":[\"Guest Post\",\"Next.js\",\"Under Review\",\"Video\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2021\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/\",\"name\":\"How To Convert Video to GIF in Next.js\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925401\/Web_Assets\/blog\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643.png?_i=AA\",\"datePublished\":\"2021-06-02T08:54:57+00:00\",\"dateModified\":\"2025-03-08T21:42:47+00:00\",\"description\":\"In this media jam, we will discuss how to build a Video to GIF converter in Next.js using Cloudinary. We will use Next.js API routes to upload the video to Cloudinary and then fetch the converted GIF. We will also see how to build a download button to download the GIF and a clear button to delete the video from Cloudinary.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925401\/Web_Assets\/blog\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925401\/Web_Assets\/blog\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643.png?_i=AA\",\"width\":1920,\"height\":909},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How To Convert Video to GIF in Next.js\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"How To Convert Video to GIF in Next.js","description":"In this media jam, we will discuss how to build a Video to GIF converter in Next.js using Cloudinary. We will use Next.js API routes to upload the video to Cloudinary and then fetch the converted GIF. We will also see how to build a download button to download the GIF and a clear button to delete the video from Cloudinary.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/","og_locale":"en_US","og_type":"article","og_title":"How To Convert Video to GIF in Next.js","og_description":"In this media jam, we will discuss how to build a Video to GIF converter in Next.js using Cloudinary. We will use Next.js API routes to upload the video to Cloudinary and then fetch the converted GIF. We will also see how to build a download button to download the GIF and a clear button to delete the video from Cloudinary.","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/","og_site_name":"Cloudinary Blog","article_published_time":"2021-06-02T08:54:57+00:00","article_modified_time":"2025-03-08T21:42:47+00:00","og_image":[{"width":1920,"height":909,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1681925401\/Web_Assets\/blog\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643-png?_i=AA","type":"image\/png"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/"},"author":{"name":"","@id":""},"headline":"How To Convert Video to GIF in Next.js","datePublished":"2021-06-02T08:54:57+00:00","dateModified":"2025-03-08T21:42:47+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/"},"wordCount":9,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925401\/Web_Assets\/blog\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643.png?_i=AA","keywords":["Guest Post","Next.js","Under Review","Video"],"inLanguage":"en-US","copyrightYear":"2021","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/","name":"How To Convert Video to GIF in Next.js","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925401\/Web_Assets\/blog\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643.png?_i=AA","datePublished":"2021-06-02T08:54:57+00:00","dateModified":"2025-03-08T21:42:47+00:00","description":"In this media jam, we will discuss how to build a Video to GIF converter in Next.js using Cloudinary. We will use Next.js API routes to upload the video to Cloudinary and then fetch the converted GIF. We will also see how to build a download button to download the GIF and a clear button to delete the video from Cloudinary.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925401\/Web_Assets\/blog\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925401\/Web_Assets\/blog\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643.png?_i=AA","width":1920,"height":909},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/how-to-convert-video-to-gif-in-next-js-with-cloudinary\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"How To Convert Video to GIF in Next.js"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":""}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925401\/Web_Assets\/blog\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643\/ecdd6d7c983a85d90ab1b570530ec416668eec08-1920x909-1_28097a7643.png?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28096","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=28096"}],"version-history":[{"count":3,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28096\/revisions"}],"predecessor-version":[{"id":37142,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28096\/revisions\/37142"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/28097"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=28096"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=28096"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=28096"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}