{"id":27886,"date":"2022-06-02T12:01:31","date_gmt":"2022-06-02T12:01:31","guid":{"rendered":"http:\/\/upload-multiple-images-to-cloudinary-with-filepond"},"modified":"2025-02-23T15:15:21","modified_gmt":"2025-02-23T23:15:21","slug":"upload-multiple-images-to-cloudinary-with-filepond","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/","title":{"rendered":"Upload Multiple Images to Cloudinary with FilePond"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>When building sites, the file upload functionality tends to be more complex than anticipated. It\u2019s no longer enough to have an input form that sends documents to some server. These days one has to consider asynchronous file uploads, progress bars, etc. Additionally, it must be abstracted and covered with a visually appealing form that users will find intuitive. This is where <a href=\"https:\/\/pqina.nl\/filepond\/\">FilePond<\/a> has found its relevance.<\/p>\n<p>Filepond provides a visually appealing file upload interface that allows you to drag and drop files. It also automatically handles the upload process and progress updates. Additionally, it provides a feature to <strong>revert<\/strong> uploads \u2014 all of which we will be exploring in this article.<\/p>\n<p>We will build a <a href=\"https:\/\/reactjs.org\/\">React<\/a> application that uses Filepond to upload multiple images to a <a href=\"https:\/\/cloudinary.com\/\">Cloudinary<\/a> store and also (should the user decide to) delete the images.<\/p>\n<p>Here is a <a href=\"https:\/\/codesandbox.io\/s\/upload-multiple-images-to-cloudinary-with-filepond-h2r7mb\">link<\/a> to the demo on CodeSandbox.<\/p>\n<\/div>\n  \n  <div class=\"wp-block-cloudinary-code-sandbox \">\n    <iframe\n      src=\"https:\/\/codesandbox.io\/embed\/upload-multiple-images-to-cloudinary-with-filepond-h2r7mb?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=&amp;moduleview=0&amp;previewwindow=&amp;view=&amp;runonclick=1\"\n      height=\"500\"\n      style=\"width: 100%;\"\n      title=\"upload-multiple-images-to-cloudinary-with-filepond-h2r7mb\"\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  <div class=\"wp-block-cloudinary-markdown \"><h2>Setting up the Project<\/h2>\n<p>Create a new React application by running the following command:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">npx create-react-app cloudinary_filepond\n<\/code><\/span><\/pre>\n<p>Next, add FilePond to the React application by running the following command:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">npm install react-filepond filepond\n<\/code><\/span><\/pre>\n<p>We\u2019ll also be using two FilePond plugins &#8211; the <a href=\"https:\/\/pqina.nl\/filepond\/docs\/api\/plugins\/image-exif-orientation\/\">Image exif-orientation<\/a>  plugin, which is used by the <a href=\"https:\/\/pqina.nl\/filepond\/docs\/api\/plugins\/image-preview\/\">Image preview<\/a> plugin to ensure that the preview of the uploaded image is displayed correctly. Add both plugins using the following command:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">npm install filepond-plugin-image-preview filepond-plugin-image-exif-orientation\n<\/code><\/span><\/pre>\n<p>For this tutorial, we will be sending images to Cloudinary via unsigned POST requests, but you need to first sign up for a <a href=\"https:\/\/cloudinary.com\/users\/register\/free\">free Cloudinary account<\/a> if you don\u2019t have one already. Displayed on your account\u2019s Management Console (aka Dashboard) are important details: your cloud name, API key, etc. We will need our account cloud name and an unsigned upload preset. To create one, log into the <a href=\"https:\/\/cloudinary.com\/console\/settings\/upload\">Management Console<\/a> and select <strong>Settings<\/strong> &gt; <strong>Upload<\/strong> and then scroll to the <strong>Upload presets<\/strong>\nsection. Create a new upload preset by clicking <strong>Add upload preset<\/strong> at the bottom of the upload preset list. In the displayed form,  make sure the <strong>Signing Mode<\/strong> is set to <strong>Unsigned<\/strong> as shown below.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/media_jams\/s_878252A36E5715818AB513809817CDE199F2CD70A8A3B4A0CDE6EC2045EA186A_1652562609487_CleanShot+2022-05-15+at+01.09.292x.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"2000\" height=\"723\"\/><\/p>\n<p>Next, in the <strong>Upload Control<\/strong> section, ensure that the <strong>Return delete token<\/strong> option is turned on. This will allow us to delete uploaded images within 10 minutes of uploading to Cloudinary.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/media_jams\/s_878252A36E5715818AB513809817CDE199F2CD70A8A3B4A0CDE6EC2045EA186A_1652562763967_CleanShot+2022-05-15+at+01.12.102x.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"2000\" height=\"789\"\/><\/p>\n<p>Click <strong>Save<\/strong> to complete the upload preset definition, then copy the upload preset name as displayed on the Upload Settings page.<\/p>\n<p>Next, we need to create some environment variables in our React application to hold our Cloudinary details. Create a new file at the root of your project called <code>.env<\/code> and add the following to it:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">REACT_APP_CLOUD_NAME = INSERT YOUR CLOUD NAME HERE\nREACT_APP_UPLOAD_PRESET = INSERT YOUR UNSIGNED UPLOAD PRESET KEY HERE\n<\/code><\/span><\/pre>\n<p>This will be used as a default when the project is set up on another system. To update your local environment, create a copy of the .env file using 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\">cp<\/span> <span class=\"hljs-selector-class\">.env<\/span> <span class=\"hljs-selector-class\">.env<\/span><span class=\"hljs-selector-class\">.local<\/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>By default, this local file is added to .gitignore and mitigates the security risk of inadvertently exposing secret credentials to the public. You can update the file with your Cloudinary cloud name and generated upload preset.<\/p>\n<p>In the <code>src<\/code> directory of the project, create a new folder named <code>cloudinary<\/code>. This folder will hold all the Cloudinary-related helper classes we will need in our components. In the <code>cloudinary<\/code> folder, create a new file called <code>cloudinaryConfig.js<\/code>. This file will give access to the environment variables and prevent repeated <code>process.env.<\/code> calls throughout the project. In <code>cloudinaryConfig.js<\/code>, add the following:<\/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\"><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> cloudName = process.env.REACT_APP_CLOUD_NAME;\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> uploadPreset = process.env.REACT_APP_UPLOAD_PRESET;\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<h2>Create Helper Class for API Requests<\/h2>\n<p>Let\u2019s write a helper function that we will use to upload images to Cloudinary and another to delete images from Cloudinary. In the <code>cloudinary<\/code> folder, create a new file named <code>cloudinaryHelper.js<\/code> and add the following code to it:<\/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> { cloudName, uploadPreset } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/cloudinaryConfig\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> baseUrl = <span class=\"hljs-string\">`https:\/\/api.cloudinary.com\/v1_1\/<span class=\"hljs-subst\">${cloudName}<\/span>`<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> makeUploadRequest = ({\n  file,\n  fieldName,\n  progressCallback,\n  successCallback,\n  errorCallback,\n}) =&gt; {\n\n  <span class=\"hljs-keyword\">const<\/span> url = <span class=\"hljs-string\">`<span class=\"hljs-subst\">${baseUrl}<\/span>\/image\/upload`<\/span>;\n\n  <span class=\"hljs-keyword\">const<\/span> formData = <span class=\"hljs-keyword\">new<\/span> FormData();\n  formData.append(fieldName, file);\n  formData.append(<span class=\"hljs-string\">\"upload_preset\"<\/span>, uploadPreset);\n\n  <span class=\"hljs-keyword\">const<\/span> request = <span class=\"hljs-keyword\">new<\/span> XMLHttpRequest();\n  request.open(<span class=\"hljs-string\">\"POST\"<\/span>, url);\n\n  request.upload.onprogress = <span class=\"hljs-function\">(<span class=\"hljs-params\">e<\/span>) =&gt;<\/span> {\n    progressCallback(e.lengthComputable, e.loaded, e.total);\n  };\n\n  request.onload = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    <span class=\"hljs-keyword\">if<\/span> (request.status &gt;= <span class=\"hljs-number\">200<\/span> &amp;&amp; request.status &lt; <span class=\"hljs-number\">300<\/span>) {\n      <span class=\"hljs-keyword\">const<\/span> { <span class=\"hljs-attr\">delete_token<\/span>: deleteToken } = <span class=\"hljs-built_in\">JSON<\/span>.parse(request.response);\n\n      successCallback(deleteToken);\n    } <span class=\"hljs-keyword\">else<\/span> {\n      errorCallback(request.responseText);\n    }\n  };\n\n  request.send(formData);\n  \n  <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    request.abort();\n  };\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> makeDeleteRequest = ({\n  token,\n  successCallback,\n  errorCallback,\n}) =&gt; {\n\n  <span class=\"hljs-keyword\">const<\/span> url = <span class=\"hljs-string\">`<span class=\"hljs-subst\">${baseUrl}<\/span>\/delete_by_token`<\/span>;\n\n  <span class=\"hljs-keyword\">const<\/span> request = <span class=\"hljs-keyword\">new<\/span> XMLHttpRequest();\n  request.open(<span class=\"hljs-string\">\"POST\"<\/span>, url);\n\n  request.setRequestHeader(<span class=\"hljs-string\">\"Content-Type\"<\/span>, <span class=\"hljs-string\">\"application\/json\"<\/span>);\n\n  request.onload = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    <span class=\"hljs-keyword\">if<\/span> (request.status &gt;= <span class=\"hljs-number\">200<\/span> &amp;&amp; request.status &lt; <span class=\"hljs-number\">300<\/span>) {\n      successCallback();\n    } <span class=\"hljs-keyword\">else<\/span> {\n      errorCallback(request.responseText);\n    }\n  };\n  request.send(<span class=\"hljs-built_in\">JSON<\/span>.stringify({ token }));\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>Requests are sent to Cloudinary using <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/XMLHttpRequest\">XMLHttpRequest<\/a> Objects. In the <code>makeUploadRequest<\/code> function, we provide a function named <code>progressCallback<\/code>, which is used to update the progress indicator of the Filepond UI. Additionally, a function is provided, which is executed when a successful response is received \u2014 <code>successCallback<\/code>. This function takes the delete token provided by Cloudinary in the response. The <code>errorCallback<\/code> function is executed if an error response is returned.<\/p>\n<p>The <code>makeUploadRequest<\/code> function returns a function that can be called if the user chooses to cancel the upload before it is completed.<\/p>\n<p>In a similar vein, the <code>makeDeleteRequest<\/code> takes a token, a <code>successCallback<\/code> function, and an <code>errorCallback<\/code> function which are executed upon receipt of successful and error responses, respectively.<\/p>\n<p>Next, update your <code>src\/App.js<\/code> file to match the following:<\/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\"><span class=\"hljs-keyword\">import<\/span> React, { useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { FilePond, registerPlugin } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react-filepond\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> FilePondPluginImageExifOrientation <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"filepond-plugin-image-exif-orientation\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> FilePondPluginImagePreview <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"filepond-plugin-image-preview\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">\"filepond-plugin-image-preview\/dist\/filepond-plugin-image-preview.css\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">\"filepond\/dist\/filepond.min.css\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> {\n  makeDeleteRequest,\n  makeUploadRequest,\n} <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/cloudinary\/cloudinaryHelper\"<\/span>;\n\nregisterPlugin(FilePondPluginImageExifOrientation, FilePondPluginImagePreview);\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">App<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> &#91;files, setFiles] = useState(&#91;]);\n\n  <span class=\"hljs-keyword\">const<\/span> revert = <span class=\"hljs-function\">(<span class=\"hljs-params\">token, successCallback, errorCallback<\/span>) =&gt;<\/span> {\n    makeDeleteRequest({\n      token,\n      successCallback,\n      errorCallback,\n    });\n  };\n\n  <span class=\"hljs-keyword\">const<\/span> process = (\n  fieldName,\n    file,\n    metadata,\n    load,\n    error,\n    progress,\n    abort,\n    transfer,\n    options\n  ) =&gt; {\n    <span class=\"hljs-keyword\">const<\/span> abortRequest = makeUploadRequest({\n      file,\n      fieldName,\n      <span class=\"hljs-attr\">successCallback<\/span>: load,\n      <span class=\"hljs-attr\">errorCallback<\/span>: error,\n      <span class=\"hljs-attr\">progressCallback<\/span>: progress,\n    });\n\n    <span class=\"hljs-keyword\">return<\/span> {\n      <span class=\"hljs-attr\">abort<\/span>: <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n        abortRequest();\n        abort();\n      },\n    };\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\">style<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">width:<\/span> \"<span class=\"hljs-attr\">80<\/span>%\", <span class=\"hljs-attr\">margin:<\/span> \"<span class=\"hljs-attr\">auto<\/span>\", <span class=\"hljs-attr\">padding:<\/span> \"<span class=\"hljs-attr\">1<\/span>%\" }}&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">FilePond<\/span>\n        <span class=\"hljs-attr\">files<\/span>=<span class=\"hljs-string\">{files}<\/span>\n        <span class=\"hljs-attr\">acceptedFileTypes<\/span>=<span class=\"hljs-string\">\"image\/*\"<\/span>\n        <span class=\"hljs-attr\">onupdatefiles<\/span>=<span class=\"hljs-string\">{setFiles}<\/span>\n        <span class=\"hljs-attr\">allowMultiple<\/span>=<span class=\"hljs-string\">{true}<\/span>\n        <span class=\"hljs-attr\">server<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">process<\/span>, <span class=\"hljs-attr\">revert<\/span> }}\n        <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"file\"<\/span>\n        <span class=\"hljs-attr\">labelIdle<\/span>=<span class=\"hljs-string\">'Drag &amp; Drop your files or &lt;span class=\"filepond--label-action\"&gt;Browse&lt;\/span&gt;'<\/span>\n      \/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  );\n}\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> App;\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>The <code>App<\/code> component has one state variable \u2014 <code>files<\/code>, which is used to keep track of the images selected by the user. Next, we declared two functions: <code>process<\/code> and <code>revert<\/code>, which handle the upload and delete operations. These functions are passed as props to the <code>Filepond<\/code> component.<\/p>\n<p>In the  <code>process<\/code> function, Filepond makes nine parameters available &#8211; however, we only need six for our use case. The <code>fieldName<\/code> and <code>file<\/code> parameters are appended to the <code>FormData<\/code> request, sent to Cloudinary. The <code>load<\/code> function is called upon successful execution of the request, and it takes a string &#8211; in our case, the delete token for the uploaded image, which is used to identify each file uniquely. When the <code>revert<\/code> function is called, Filepond knows exactly which image to delete. The <code>progress<\/code> function is used to update the progress bar, while the <code>error<\/code> function takes a string that displays as an error message. Just as we did for the <code>makeUploadRequest<\/code> function, the <code>process<\/code> function returns a function that is used to abort the upload process.<\/p>\n<p>With this in place, our application is ready to test. Start the application using the following command:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">npm start\n<\/code><\/span><\/pre>\n<h2>Disabling React Strict Mode<\/h2>\n<p>When you run your application, you may see an error message in the browser console similar to the one shown below:<\/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\">Uncaught <span class=\"hljs-built_in\">TypeError<\/span>: Cannot read properties <span class=\"hljs-keyword\">of<\/span> <span class=\"hljs-literal\">null<\/span> (reading <span class=\"hljs-string\">'insertBefore'<\/span>)\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>Disable React strict mode in the <code>src\/Index.js<\/code> to get rid of the error. To do this, open the <code>src\/Index.js<\/code> and update it to match the following:<\/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\">import<\/span> React <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'react'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> ReactDOM <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'react-dom\/client'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'.\/index.css'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> App <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'.\/App'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> reportWebVitals <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'.\/reportWebVitals'<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> root = ReactDOM.createRoot(<span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">'root'<\/span>));\nroot.render(<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">App<\/span> \/&gt;<\/span><\/span>);\n\nreportWebVitals();\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><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/media_jams\/s_878252A36E5715818AB513809817CDE199F2CD70A8A3B4A0CDE6EC2045EA186A_1652701739907_CleanShot+2022-05-16+at+15.47.39.gif\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"800\" height=\"368\"\/><\/p>\n<p>Find the complete project <a href=\"https:\/\/github.com\/ifeoma-imoh\/upload-to-cloudinary-with-filepond\">here<\/a> on GitHub.<\/p>\n<h2>Conclusion<\/h2>\n<p>In this article, we looked at how we can combine Filepond and Cloudinary to simplify the process of uploading multiple files while providing an intuitive interface with the ability to revert uploads &#8211; even after completion. We achieved a balance between complex functionality and code maintainability by leveraging them.<\/p>\n<p><strong>Resources you may find helpful:<\/strong><\/p>\n<ul>\n<li>\n<a href=\"https:\/\/pqina.nl\/filepond\/\">FilePond Documentation<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/pqina.nl\/filepond\/plugins\/\">FilePond Plugins<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/cloudinary.com\/documentation\/image_upload_api_reference\">Cloudinary Upload API Reference<\/a>\n<\/li>\n<\/ul>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":27887,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[134,370,246,371,373],"class_list":["post-27886","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-guest-post","tag-image","tag-react","tag-under-review","tag-upload"],"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>Upload Multiple Images to Cloudinary with FilePond<\/title>\n<meta name=\"description\" content=\"In this post, we will build a React application that uses FilePond to upload multiple images to Cloudinary and also (should the user decide to) delete the images.\" \/>\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\/upload-multiple-images-to-cloudinary-with-filepond\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Upload Multiple Images to Cloudinary with FilePond\" \/>\n<meta property=\"og:description\" content=\"In this post, we will build a React application that uses FilePond to upload multiple images to Cloudinary and also (should the user decide to) delete the images.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-06-02T12:01:31+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-02-23T23:15:21+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1681925993\/Web_Assets\/blog\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c-png?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"1280\" \/>\n\t<meta property=\"og:image:height\" content=\"720\" \/>\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\/upload-multiple-images-to-cloudinary-with-filepond\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Upload Multiple Images to Cloudinary with FilePond\",\"datePublished\":\"2022-06-02T12:01:31+00:00\",\"dateModified\":\"2025-02-23T23:15:21+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/\"},\"wordCount\":7,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925993\/Web_Assets\/blog\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c.png?_i=AA\",\"keywords\":[\"Guest Post\",\"Image\",\"React\",\"Under Review\",\"Upload\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2022\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/\",\"name\":\"Upload Multiple Images to Cloudinary with FilePond\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925993\/Web_Assets\/blog\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c.png?_i=AA\",\"datePublished\":\"2022-06-02T12:01:31+00:00\",\"dateModified\":\"2025-02-23T23:15:21+00:00\",\"description\":\"In this post, we will build a React application that uses FilePond to upload multiple images to Cloudinary and also (should the user decide to) delete the images.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925993\/Web_Assets\/blog\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925993\/Web_Assets\/blog\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c.png?_i=AA\",\"width\":1280,\"height\":720},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Upload Multiple Images to Cloudinary with FilePond\"}]},{\"@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":"Upload Multiple Images to Cloudinary with FilePond","description":"In this post, we will build a React application that uses FilePond to upload multiple images to Cloudinary and also (should the user decide to) delete the images.","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\/upload-multiple-images-to-cloudinary-with-filepond\/","og_locale":"en_US","og_type":"article","og_title":"Upload Multiple Images to Cloudinary with FilePond","og_description":"In this post, we will build a React application that uses FilePond to upload multiple images to Cloudinary and also (should the user decide to) delete the images.","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/","og_site_name":"Cloudinary Blog","article_published_time":"2022-06-02T12:01:31+00:00","article_modified_time":"2025-02-23T23:15:21+00:00","og_image":[{"width":1280,"height":720,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1681925993\/Web_Assets\/blog\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c-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\/upload-multiple-images-to-cloudinary-with-filepond\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/"},"author":{"name":"","@id":""},"headline":"Upload Multiple Images to Cloudinary with FilePond","datePublished":"2022-06-02T12:01:31+00:00","dateModified":"2025-02-23T23:15:21+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/"},"wordCount":7,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925993\/Web_Assets\/blog\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c.png?_i=AA","keywords":["Guest Post","Image","React","Under Review","Upload"],"inLanguage":"en-US","copyrightYear":"2022","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/","name":"Upload Multiple Images to Cloudinary with FilePond","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925993\/Web_Assets\/blog\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c.png?_i=AA","datePublished":"2022-06-02T12:01:31+00:00","dateModified":"2025-02-23T23:15:21+00:00","description":"In this post, we will build a React application that uses FilePond to upload multiple images to Cloudinary and also (should the user decide to) delete the images.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925993\/Web_Assets\/blog\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925993\/Web_Assets\/blog\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c.png?_i=AA","width":1280,"height":720},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/upload-multiple-images-to-cloudinary-with-filepond\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Upload Multiple Images to Cloudinary with FilePond"}]},{"@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\/v1681925993\/Web_Assets\/blog\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c\/7ec5e066c41c1351f3354afa17c452162ec36595-1280x720-1_278876e79c.png?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27886","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=27886"}],"version-history":[{"count":1,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27886\/revisions"}],"predecessor-version":[{"id":36988,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27886\/revisions\/36988"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/27887"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=27886"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=27886"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=27886"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}