{"id":27993,"date":"2022-06-09T09:06:22","date_gmt":"2022-06-09T09:06:22","guid":{"rendered":"http:\/\/add-tags-to-images-before-upload-with-imagga"},"modified":"2025-03-23T11:38:21","modified_gmt":"2025-03-23T18:38:21","slug":"add-tags-to-images-before-upload-with-imagga","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/","title":{"rendered":"Add Tags to Images before Upload with Imagga"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>Image tagging is the process of labeling images based on figures within that image &#8211; the labels are known as tags. This has the obvious benefit of providing some level of organization to an image library, as it is easier to find images with tags. Additionally, image tag metadata can be used for a variety of purposes ranging from web accessibility to <a href=\"https:\/\/www.contentful.com\/blog\/tag\/seo\/\">SEO<\/a>. The release of <a href=\"https:\/\/cloudinary.com\/glossary\/image-tagging\">image tagging<\/a> software makes it possible to automate the tagging process, thus enjoying all these benefits while saving valuable time.<\/p>\n<p>In this tutorial, we will use <a href=\"https:\/\/imagga.com\/\">Imagga<\/a> to add tags to images that have been uploaded to our Cloudinary store. Additionally, we will render the images in a gallery, providing a list of tags that the user can click to filter the rendered images. Cloudinary provides an <a href=\"https:\/\/cloudinary.com\/documentation\/imagga_auto_tagging_addon\">add-on<\/a> for Imagga\u2019s automatic image tagging capabilities, fully integrated into its image management and transformation pipeline, and that is what we will be using.<\/p>\n<p>For UI components in our application, we will use <a href=\"https:\/\/ant.design\/\">antd<\/a> while <a href=\"https:\/\/axios-http.com\/\">axios<\/a> will be used for uploading our images to our Cloudinary store. <a href=\"https:\/\/v5.reactrouter.com\/web\/guides\/quick-start\">React router<\/a> will be used to transition between pages.<\/p>\n<p>Here is a <a href=\"https:\/\/codesandbox.io\/s\/cloudinary-imagga-auto-tagging-hk7q46\">link<\/a> to the demo CodeSandbox.<\/p>\n<\/div>\n\n\n  <div class=\"wp-block-cloudinary-code-sandbox \">\n    <iframe\n      src=\"https:\/\/codesandbox.io\/embed\/cloudinary-imagga-auto-tagging-hk7q46?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=\"cloudinary-imagga-auto-tagging-hk7q46\"\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>Project Setup<\/h2>\n<p>Create a new React app using the following command:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">npx create-react-app cloudinary-imagga\n<\/code><\/span><\/pre>\n<p>Next, add the project dependencies 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\">npm<\/span> <span class=\"hljs-selector-tag\">install<\/span> <span class=\"hljs-selector-tag\">antd<\/span> <span class=\"hljs-keyword\">@ant-design<\/span>\/icons axios react-router-dom cloudinary-react\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>Next, we need to import the antd CSS. To do this, open <code>src\/App.css<\/code> and edit its content to match the following:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-keyword\">@import<\/span> <span class=\"hljs-string\">\"~antd\/dist\/antd.css\"<\/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\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h2>Setting up Cloudinary<\/h2>\n<p>To use Cloudinary\u2019s provisioned services, you need to 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.<\/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_1654519184566_CleanShot+2022-06-06+at+13.39.052x.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"2000\" height=\"741\"\/><\/p>\n<p>For this tutorial, we will be sending images to Cloudinary via unsigned POST requests. To do this, we need our account cloud name and an unsigned <a href=\"https:\/\/cloudinary.com\/documentation\/upload_presets\">upload preset<\/a>. 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_1654519595003_CleanShot+2022-06-06+at+13.46.022x.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"2000\" height=\"720\"\/><\/p>\n<p>In the <strong>Media Analysis and AI<\/strong> section, ensure that <strong>Imagga Auto Tagging<\/strong> is selected. With the selection of Imagga auto tagging, you also have the option to specify a confidence level threshold for generated tags.<\/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_1654520890784_CleanShot+2022-06-06+at+14.07.212x.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"2000\" height=\"799\"\/><\/p>\n<p>Click <strong>Save<\/strong> to complete the upload preset definition, then copy the upload preset name as displayed on the <strong>Upload<\/strong> Settings page.<\/p>\n<p>Next, let\u2019s create environment variables to hold the details of our Cloudinary account. Create a new file called <code>.env<\/code> at the root of your project and add the following 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\">REACT_APP_CLOUD_NAME = <span class=\"hljs-string\">\"INSERT YOUR CLOUD NAME HERE\"<\/span>;\nREACT_APP_UPLOAD_PRESET = <span class=\"hljs-string\">\"INSERT YOUR UNSIGNED UPLOAD PRESET KEY HERE\"<\/span>;\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>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 <code>.env<\/code> file using the following command:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" 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-4\"><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 resides in the .gitignore folder, mitigating the security risk of inadvertently exposing secret credentials to the public. You can update the <code>.env.local<\/code> file with your Cloudinary credentials.<\/p>\n<p>When we sign up for a Cloudinary account, we don\u2019t immediately get access to the <a href=\"https:\/\/cloudinary.com\/documentation\/cloudinary_add_ons\">add-ons<\/a>. To access the Imagga Auto Tagging add-on, you need to register. Each add-on gives us a variety of plans and their associated pricing. Fortunately, most of them also have free plans, which is what we will be using for our application.<\/p>\n<p>To register, click on the <strong>Add-ons<\/strong> link in the header of the management console on your Cloudinary dashboard.<\/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_1654520494728_CleanShot+2022-06-06+at+14.01.052x.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"2000\" height=\"732\"\/><\/p>\n<p>You should see the Cloudinary Add-ons page with a list of all available add-ons. Select the <strong>Imagga Auto Tagging add-on<\/strong> and subscribe to the free plan, which allows 15 artworks monthly, or you can select your preferred plan.<\/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_1654520409198_CleanShot+2022-06-06+at+13.59.372x.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"2000\" height=\"729\"\/><\/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. Add the following to the<code>cloudinaryConfig.js<\/code> file:<\/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-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<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> defaultUploadTag = <span class=\"hljs-string\">\"cloudinary_imagga\"<\/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<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 an image from Cloudinary. In the <code>cloudinary<\/code> folder, create a new file named <code>cloudinaryHelper.js<\/code> and add the following to it:<\/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> axios <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"axios\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { cloudName, defaultUploadTag, uploadPreset } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/cloudinaryConfig\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> uploadImage = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ file, successCallback }<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> url = <span class=\"hljs-string\">`https:\/\/api.cloudinary.com\/v1_1\/<span class=\"hljs-subst\">${cloudName}<\/span>\/image\/upload`<\/span>;\n  <span class=\"hljs-keyword\">const<\/span> data = <span class=\"hljs-keyword\">new<\/span> FormData();\n  data.append(<span class=\"hljs-string\">\"file\"<\/span>, file);\n  data.append(<span class=\"hljs-string\">\"upload_preset\"<\/span>, uploadPreset);\n  data.append(<span class=\"hljs-string\">\"tags\"<\/span>, defaultUploadTag);\n\n  axios\n    .post(url, data, {\n      <span class=\"hljs-attr\">headers<\/span>: {\n        <span class=\"hljs-string\">\"Content-Type\"<\/span>: <span class=\"hljs-string\">\"multipart\/form-data\"<\/span>,\n      },\n    })\n    .then(<span class=\"hljs-function\">(<span class=\"hljs-params\">response<\/span>) =&gt;<\/span> successCallback(response.data));\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> getImages = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ successCallback, imageTag }<\/span>) =&gt;<\/span> {\n  axios\n    .get(\n      <span class=\"hljs-string\">`https:\/\/res.cloudinary.com\/<span class=\"hljs-subst\">${cloudName}<\/span>\/image\/list\/<span class=\"hljs-subst\">${\n        imageTag || defaultUploadTag\n      }<\/span>.json`<\/span>\n    )\n    .then(<span class=\"hljs-function\">(<span class=\"hljs-params\">response<\/span>) =&gt;<\/span> successCallback(response.data.resources));\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>The <code>uploadImage<\/code> function is used to upload an image to our Cloudinary store. In addition to the image file and the upload preset, we append a tag to the image. This will allow us to retrieve all the images without a backend API, as we will see later.<\/p>\n<p>In the <code>getImages<\/code> function, we use the <a href=\"https:\/\/cloudinary.com\/documentation\/advanced_url_delivery_options#client_side_asset_lists\">Client-side asset lists<\/a> feature to retrieve the list of images with our set tag. Where a tag is specified in the <code>imageTag<\/code> key, a subset of images &#8211; classified by Imagga to match that tag; are retrieved from our Cloudinary store. If no tag is provided, then the default tag is used to retrieve all the images tagged by our application during the upload process.<\/p>\n<p><strong>NOTE<\/strong>: To ensure that this feature is available on your Cloudinary account, you must ensure that the Resource list option is enabled. By default, the list delivery type is restricted. To enable it, open the Security settings in your Management console and clear the <strong>Resource list<\/strong> item under <strong>Restricted image types<\/strong>. You may want to clear this option only temporarily, as needed. Alternatively, you can bypass this (and any) delivery type restriction using a signed URL.<\/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_1654525600000_CleanShot+2022-06-06+at+15.26.142x.png\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"2000\" height=\"1045\"\/><\/p>\n<h2>Adding Hooks<\/h2>\n<p>Next, let\u2019s add some hooks to provide the functionality for our image upload. Create a new folder called <code>hooks<\/code> in the\u2019 src\u2019 directory. In the <code>src\/hooks<\/code> folder, create a new file named <code>useFilePreview.js<\/code> and add the following to it:<\/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\">import<\/span> { Modal } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"antd\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> useFilePreview = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> &#91;previewVisibility, setPreviewVisibility] = useState(<span class=\"hljs-literal\">false<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> &#91;previewImage, setPreviewImage] = useState(<span class=\"hljs-literal\">null<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> &#91;previewTitle, setPreviewTitle] = useState(<span class=\"hljs-string\">\"\"<\/span>);\n\n  <span class=\"hljs-keyword\">const<\/span> getBase64Representation = <span class=\"hljs-function\">(<span class=\"hljs-params\">file<\/span>) =&gt;<\/span>\n    <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Promise<\/span>(<span class=\"hljs-function\">(<span class=\"hljs-params\">resolve, reject<\/span>) =&gt;<\/span> {\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> resolve(reader.result);\n      reader.onerror = <span class=\"hljs-function\">(<span class=\"hljs-params\">error<\/span>) =&gt;<\/span> reject(error);\n    });\n\n  <span class=\"hljs-keyword\">const<\/span> handlePreview = <span class=\"hljs-keyword\">async<\/span> (file) =&gt; {\n    <span class=\"hljs-keyword\">if<\/span> (!file.url &amp;&amp; !file.preview) {\n      file.preview = <span class=\"hljs-keyword\">await<\/span> getBase64Representation(file.originFileObj);\n    }\n    setPreviewImage(file.url || file.preview);\n    setPreviewVisibility(<span class=\"hljs-literal\">true<\/span>);\n    setPreviewTitle(\n      file.name || file.url.substring(file.url.lastIndexOf(<span class=\"hljs-string\">\"\/\"<\/span>) + <span class=\"hljs-number\">1<\/span>)\n    );\n  };\n\n  <span class=\"hljs-keyword\">const<\/span> hidePreview = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    setPreviewVisibility(<span class=\"hljs-literal\">false<\/span>);\n  };\n\n  <span class=\"hljs-keyword\">const<\/span> previewContent = (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Modal<\/span>\n      <span class=\"hljs-attr\">visible<\/span>=<span class=\"hljs-string\">{previewVisibility}<\/span>\n      <span class=\"hljs-attr\">title<\/span>=<span class=\"hljs-string\">{previewTitle}<\/span>\n      <span class=\"hljs-attr\">footer<\/span>=<span class=\"hljs-string\">{null}<\/span>\n      <span class=\"hljs-attr\">onCancel<\/span>=<span class=\"hljs-string\">{hidePreview}<\/span>\n    &gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">{previewTitle}<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">width:<\/span> \"<span class=\"hljs-attr\">100<\/span>%\" }} <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{previewImage}<\/span> \/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Modal<\/span>&gt;<\/span><\/span>\n  );\n\n  <span class=\"hljs-keyword\">return<\/span> &#91;handlePreview, previewContent];\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> useFilePreview;\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>This hook provides the functionality to allow us to preview selected images before uploading by generating a base64 string representation of the file and rendering it in a modal when required.<\/p>\n<p>Next, in the <code>src\/hooks<\/code> directory, create a new file called <code>useFileSelection.js<\/code> and add the following to it:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-8\" 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> { message } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"antd\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { uploadImage } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/cloudinary\/cloudinaryHelper\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { defaultUploadTag } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/cloudinary\/cloudinaryConfig\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { useNavigate } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react-router-dom\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> useFileSelection = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> &#91;selectedFiles, setSelectedFiles] = useState(&#91;]);\n  <span class=\"hljs-keyword\">const<\/span> &#91;isUploading, setIsUploading] = useState(<span class=\"hljs-literal\">false<\/span>);\n\n  <span class=\"hljs-keyword\">const<\/span> navigate = useNavigate();\n\n  <span class=\"hljs-keyword\">const<\/span> addFile = <span class=\"hljs-function\">(<span class=\"hljs-params\">file<\/span>) =&gt;<\/span> {\n    setSelectedFiles(<span class=\"hljs-function\">(<span class=\"hljs-params\">currentSelection<\/span>) =&gt;<\/span> &#91;...currentSelection, file]);\n  };\n\n  <span class=\"hljs-keyword\">const<\/span> removeFile = <span class=\"hljs-function\">(<span class=\"hljs-params\">file<\/span>) =&gt;<\/span> {\n    setSelectedFiles(<span class=\"hljs-function\">(<span class=\"hljs-params\">currentSelection<\/span>) =&gt;<\/span> {\n      <span class=\"hljs-keyword\">const<\/span> newSelection = currentSelection.slice();\n      <span class=\"hljs-keyword\">const<\/span> fileIndex = currentSelection.indexOf(file);\n      newSelection.splice(fileIndex, <span class=\"hljs-number\">1<\/span>);\n      <span class=\"hljs-keyword\">return<\/span> newSelection;\n    });\n  };\n\n  <span class=\"hljs-keyword\">const<\/span> uploadSelection = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    <span class=\"hljs-keyword\">if<\/span> (selectedFiles.length === <span class=\"hljs-number\">0<\/span>) {\n      message.error(<span class=\"hljs-string\">\"You need to select at least one image\"<\/span>);\n      <span class=\"hljs-keyword\">return<\/span>;\n    }\n\n    setIsUploading(<span class=\"hljs-literal\">true<\/span>);\n    <span class=\"hljs-keyword\">const<\/span> uploadResults = &#91;];\n    <span class=\"hljs-keyword\">const<\/span> returnedTags = &#91;];\n\n    selectedFiles.forEach(<span class=\"hljs-function\">(<span class=\"hljs-params\">file<\/span>) =&gt;<\/span> {\n      uploadImage({\n        file,\n        <span class=\"hljs-attr\">successCallback<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">response<\/span>) =&gt;<\/span> {\n          returnedTags.push(...response.tags);\n          uploadResults.push(response);\n          <span class=\"hljs-keyword\">if<\/span> (uploadResults.length === selectedFiles.length) {\n            setIsUploading(<span class=\"hljs-literal\">false<\/span>);\n            <span class=\"hljs-keyword\">const<\/span> tags = <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Set<\/span>(returnedTags);\n            tags.delete(defaultUploadTag);\n            message.success(<span class=\"hljs-string\">\"Images uploaded successfully\"<\/span>);\n            navigate(<span class=\"hljs-string\">\"\/gallery\"<\/span>, { <span class=\"hljs-attr\">state<\/span>: &#91;...tags] });\n          }\n        },\n      });\n    });\n  };\n\n  <span class=\"hljs-keyword\">return<\/span> &#91;addFile, removeFile, isUploading, uploadSelection];\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> useFileSelection;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><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>This hook helps us keep track of the files selected we want to upload, as well as add or remove files. It also provides a function that allows us to upload the selected files to Cloudinary using the <code>uploadImage<\/code> function declared earlier. In the <code>successCallback<\/code> provided, we retrieve the tags provided from the Cloudinary upload response and add them to an array. Once all the images have been uploaded, we generate a <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Set\">Set<\/a> object, which removes duplicate tags and passes the destructured Set object to the component rendered by the <code>\/gallery<\/code> route. This is a workaround to access the generated tags temporarily.<\/p>\n<p><strong>NOTE:<\/strong> Cloudinary also provides an <a href=\"https:\/\/cloudinary.com\/documentation\/admin_api\">Admin API<\/a>, which among other things, can retrieve all the <a href=\"https:\/\/cloudinary.com\/documentation\/admin_api#tags\">tags<\/a> currently used for a specified resource type.<\/p>\n<p>With our hooks in place, let\u2019s build our components.<\/p>\n<h2>Adding Components<\/h2>\n<p>In the <code>src<\/code> directory, create a new folder named <code>components<\/code>. In the <code>src\/components<\/code> folder, create a new file named <code>DragAndDrop.js<\/code> and add the following to it:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-9\" 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> { Upload } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"antd\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { PlusOutlined } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@ant-design\/icons\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> useFilePreview <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/hooks\/useFilePreview\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> { Dragger } = Upload;\n\n<span class=\"hljs-keyword\">const<\/span> DragAndDrop = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ addFile, removeFile }<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> &#91;handlePreview, previewContent] = useFilePreview();\n\n  <span class=\"hljs-keyword\">const<\/span> beforeUploadHandler = <span class=\"hljs-function\">(<span class=\"hljs-params\">file<\/span>) =&gt;<\/span> {\n    addFile(file);\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">false<\/span>;\n  };\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Dragger<\/span>\n        <span class=\"hljs-attr\">multiple<\/span>=<span class=\"hljs-string\">{true}<\/span>\n        <span class=\"hljs-attr\">onRemove<\/span>=<span class=\"hljs-string\">{removeFile}<\/span>\n        <span class=\"hljs-attr\">showUploadList<\/span>=<span class=\"hljs-string\">{true}<\/span>\n        <span class=\"hljs-attr\">listType<\/span>=<span class=\"hljs-string\">\"picture-card\"<\/span>\n        <span class=\"hljs-attr\">beforeUpload<\/span>=<span class=\"hljs-string\">{beforeUploadHandler}<\/span>\n        <span class=\"hljs-attr\">onPreview<\/span>=<span class=\"hljs-string\">{handlePreview}<\/span>\n        <span class=\"hljs-attr\">accept<\/span>=<span class=\"hljs-string\">\"image\/*\"<\/span>\n      &gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"ant-upload-drag-icon\"<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">PlusOutlined<\/span> \/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"ant-upload-text\"<\/span>&gt;<\/span>\n          Click this area or drag files to upload\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Dragger<\/span>&gt;<\/span>\n      {previewContent}\n    <span class=\"hljs-tag\">&lt;\/&gt;<\/span><\/span>\n  );\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> DragAndDrop;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><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>DragAndDrop<\/code> component wraps the AntD <a href=\"https:\/\/ant.design\/components\/upload\/\">Upload<\/a> component and uses the <code>useFilePreview<\/code> hook to add the required functionality for us to preview files before uploading.<\/p>\n<p>Next, in the <code>src\/components<\/code> directory, create a new file named <code>ImageUpload.js<\/code> and add the following to it:<\/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\">import<\/span> useFileSelection <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/hooks\/useFileSelection\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { Button, Card } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"antd\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> DragAndDrop <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/DragAndDrop\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> ImageUpload = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> &#91;addFile, removeFile, isUploading, uploadSelection] =\n    useFileSelection();\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Card<\/span>\n      <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">margin:<\/span> \"<span class=\"hljs-attr\">auto<\/span>\", <span class=\"hljs-attr\">width:<\/span> \"<span class=\"hljs-attr\">50<\/span>%\" }}\n      <span class=\"hljs-attr\">actions<\/span>=<span class=\"hljs-string\">{&#91;<\/span>\n        &lt;<span class=\"hljs-attr\">Button<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"primary\"<\/span> <span class=\"hljs-attr\">loading<\/span>=<span class=\"hljs-string\">{isUploading}<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{uploadSelection}<\/span>&gt;<\/span>\n          Submit\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Button<\/span>&gt;<\/span>,\n      ]}\n    &gt;\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">DragAndDrop<\/span> <span class=\"hljs-attr\">addFile<\/span>=<span class=\"hljs-string\">{addFile}<\/span> <span class=\"hljs-attr\">removeFile<\/span>=<span class=\"hljs-string\">{removeFile}<\/span> \/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Card<\/span>&gt;<\/span><\/span>\n  );\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> ImageUpload;\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>This component builds on the <code>DragAndDrop<\/code> component and adds a submit button which triggers the upload process. The <code>useFileSelection<\/code> hook provides the functionality for adding files, removing files, and uploading the selection.<\/p>\n<p>Next, let\u2019s create a component to render the tags returned by Imagga. In the <code>src\/components<\/code> folder, create a new file named <code>Tags.js<\/code> and add the following to it:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-11\" 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> { Tag } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"antd\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> tagColours = &#91;\n  <span class=\"hljs-string\">\"magenta\"<\/span>,\n  <span class=\"hljs-string\">\"red\"<\/span>,\n  <span class=\"hljs-string\">\"volcano\"<\/span>,\n  <span class=\"hljs-string\">\"orange\"<\/span>,\n  <span class=\"hljs-string\">\"gold\"<\/span>,\n  <span class=\"hljs-string\">\"lime\"<\/span>,\n  <span class=\"hljs-string\">\"green\"<\/span>,\n  <span class=\"hljs-string\">\"cyan\"<\/span>,\n  <span class=\"hljs-string\">\"blue\"<\/span>,\n  <span class=\"hljs-string\">\"geekblue\"<\/span>,\n  <span class=\"hljs-string\">\"purple\"<\/span>,\n];\n\n<span class=\"hljs-keyword\">const<\/span> getTagColour = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span>\n  tagColours&#91;<span class=\"hljs-built_in\">Math<\/span>.floor(<span class=\"hljs-built_in\">Math<\/span>.random() * tagColours.length)];\n\n<span class=\"hljs-keyword\">const<\/span> Tags = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ tags, setActiveTag }<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-keyword\">if<\/span> (tags.length === <span class=\"hljs-number\">0<\/span>) {\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"xml\"><span class=\"hljs-tag\">&lt;&gt;<\/span><span class=\"hljs-tag\">&lt;\/&gt;<\/span><\/span>;\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\">marginBottom:<\/span> \"<span class=\"hljs-attr\">5px<\/span>\" }}&gt;<\/span>\n      {tags.map((tag, index) =&gt; (\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Tag<\/span>\n          <span class=\"hljs-attr\">color<\/span>=<span class=\"hljs-string\">{getTagColour()}<\/span>\n          <span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{index}<\/span>\n          <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">cursor:<\/span> \"<span class=\"hljs-attr\">pointer<\/span>\", <span class=\"hljs-attr\">margin:<\/span> \"<span class=\"hljs-attr\">5px<\/span>\" }}\n        &gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>\n            <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{()<\/span> =&gt;<\/span> {\n              setActiveTag(tag);\n            }}\n          &gt;\n            {tag}\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Tag<\/span>&gt;<\/span>\n      ))}\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  );\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> Tags;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><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>We start by declaring an array of colors and a function to return a random color from the array.<\/p>\n<p>The <code>Tags<\/code> component takes two props &#8211; the <code>tags<\/code> array and a <code>setActiveTag<\/code> callback which is triggered when a tag is clicked.<\/p>\n<p>Next, we iterate through the tags and render an AntD <a href=\"https:\/\/ant.design\/components\/tag\/\">Tag<\/a> component with an <code>onClick<\/code> handler that calls the <code>setActiveTag<\/code> function.<\/p>\n<p>The next component we will build is one to display the uploaded images. In the <code>src\/components<\/code> folder, create a new file named <code>Gallery.js<\/code> and add the following to it:<\/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\">import<\/span> { getImages } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/cloudinary\/cloudinaryHelper\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { useEffect, useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { Image, Transformation } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"cloudinary-react\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { cloudName, uploadPreset } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/cloudinary\/cloudinaryConfig\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { useLocation } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react-router-dom\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { Row, Col } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"antd\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> Tags <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/Tags\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> Gallery = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> &#91;images, setImages] = useState(&#91;]);\n  <span class=\"hljs-keyword\">const<\/span> location = useLocation();\n  <span class=\"hljs-keyword\">const<\/span> uploadResultTags = location.state ?? &#91;];\n  <span class=\"hljs-keyword\">const<\/span> tags = &#91;<span class=\"hljs-string\">\"All\"<\/span>, ...uploadResultTags];\n  <span class=\"hljs-keyword\">const<\/span> &#91;activeTag, setActiveTag] = useState(<span class=\"hljs-string\">\"All\"<\/span>);\n\n  useEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    getImages({\n      <span class=\"hljs-attr\">successCallback<\/span>: setImages,\n      <span class=\"hljs-attr\">imageTag<\/span>: activeTag === <span class=\"hljs-string\">\"All\"<\/span> ? <span class=\"hljs-literal\">null<\/span> : activeTag,\n    });\n  }, &#91;activeTag]);\n\n  useEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    getImages({\n      <span class=\"hljs-attr\">successCallback<\/span>: setImages,\n    });\n  }, &#91;]);\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Row<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Tags<\/span> <span class=\"hljs-attr\">tags<\/span>=<span class=\"hljs-string\">{tags}<\/span> <span class=\"hljs-attr\">setActiveTag<\/span>=<span class=\"hljs-string\">{setActiveTag}<\/span> \/&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Row<\/span>&gt;<\/span>\n\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Row<\/span> <span class=\"hljs-attr\">gutter<\/span>=<span class=\"hljs-string\">{&#91;0,<\/span> <span class=\"hljs-attr\">16<\/span>]} <span class=\"hljs-attr\">align<\/span>=<span class=\"hljs-string\">\"middle\"<\/span>&gt;<\/span>\n        {images.map((image, index) =&gt; (\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Col<\/span> <span class=\"hljs-attr\">span<\/span>=<span class=\"hljs-string\">{6}<\/span> <span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{index}<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Image<\/span>\n              <span class=\"hljs-attr\">publicId<\/span>=<span class=\"hljs-string\">{image.public_id}<\/span>\n              <span class=\"hljs-attr\">cloudName<\/span>=<span class=\"hljs-string\">{cloudName}<\/span>\n              <span class=\"hljs-attr\">upload_preset<\/span>=<span class=\"hljs-string\">{uploadPreset}<\/span>\n              <span class=\"hljs-attr\">secure<\/span>=<span class=\"hljs-string\">{true}<\/span>\n              <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">{image.originalFilename}<\/span>\n            &gt;<\/span>\n              <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Transformation<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">{300}<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">{300}<\/span> <span class=\"hljs-attr\">crop<\/span>=<span class=\"hljs-string\">\"scale\"<\/span> \/&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Image<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Col<\/span>&gt;<\/span>\n        ))}\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Row<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/&gt;<\/span><\/span>\n  );\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> Gallery;\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>Upon mounting, this component retrieves all the images uploaded (using the default upload tag) and renders them via the functionality provided by the Cloudinary React SDK. Additionally, it renders the tags passed to it by the <code>useFileSelection<\/code> hook and listen for changes to the active tag &#8211; in the event of a change, a request is made to pull images corresponding to the selected tag.<\/p>\n<p>To make navigation between the <code>ImageUpload<\/code> and <code>Gallery<\/code> components easier, let\u2019s add a menu to our application. In the <code>src\/components<\/code> folder, create a new file named <code>Menu.js<\/code> and add the following to it:<\/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-keyword\">import<\/span> { Menu <span class=\"hljs-keyword\">as<\/span> AntDMenu } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"antd\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { Link } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react-router-dom\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { PictureOutlined, UploadOutlined } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@ant-design\/icons\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> Menu = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> &#91;currentlySelected, setCurrentlySelected] = useState(<span class=\"hljs-string\">\"upload\"<\/span>);\n\n  <span class=\"hljs-keyword\">const<\/span> handleMenuSelection = <span class=\"hljs-function\">(<span class=\"hljs-params\">e<\/span>) =&gt;<\/span> {\n    setCurrentlySelected(e.key);\n  };\n\n  <span class=\"hljs-keyword\">const<\/span> items = &#91;\n    {\n      <span class=\"hljs-attr\">label<\/span>: <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Link<\/span> <span class=\"hljs-attr\">to<\/span>=<span class=\"hljs-string\">\"\/\"<\/span>&gt;<\/span>Upload Images<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Link<\/span>&gt;<\/span><\/span>,\n      <span class=\"hljs-attr\">key<\/span>: <span class=\"hljs-string\">\"upload\"<\/span>,\n      <span class=\"hljs-attr\">icon<\/span>: <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">UploadOutlined<\/span> \/&gt;<\/span><\/span>,\n    },\n    {\n      <span class=\"hljs-attr\">label<\/span>: <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Link<\/span> <span class=\"hljs-attr\">to<\/span>=<span class=\"hljs-string\">\"\/gallery\"<\/span>&gt;<\/span>Gallery<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Link<\/span>&gt;<\/span><\/span>,\n      <span class=\"hljs-attr\">key<\/span>: <span class=\"hljs-string\">\"gallery\"<\/span>,\n      <span class=\"hljs-attr\">icon<\/span>: <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">PictureOutlined<\/span> \/&gt;<\/span><\/span>,\n    },\n  ];\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">AntDMenu<\/span>\n      <span class=\"hljs-attr\">mode<\/span>=<span class=\"hljs-string\">\"horizontal\"<\/span>\n      <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{handleMenuSelection}<\/span>\n      <span class=\"hljs-attr\">selectedKeys<\/span>=<span class=\"hljs-string\">{&#91;currentlySelected]}<\/span>\n      <span class=\"hljs-attr\">items<\/span>=<span class=\"hljs-string\">{items}<\/span>\n    \/&gt;<\/span><\/span>\n  );\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> Menu;\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<h2>Add Routing<\/h2>\n<p>With our components in place, let\u2019s add routing to handle the switching between components based on the selected route. In the <code>src<\/code> folder, create a new file named <code>routes.js<\/code> and add the following to it:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-14\" 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> Gallery <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/components\/Gallery\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> ImageUpload <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/components\/ImageUpload\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> routes = &#91;\n  {\n    <span class=\"hljs-attr\">path<\/span>: <span class=\"hljs-string\">\"\/\"<\/span>,\n    <span class=\"hljs-attr\">element<\/span>: <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ImageUpload<\/span> \/&gt;<\/span><\/span>,\n  },\n  {\n    <span class=\"hljs-attr\">path<\/span>: <span class=\"hljs-string\">\"\/gallery\"<\/span>,\n    <span class=\"hljs-attr\">element<\/span>: <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Gallery<\/span> \/&gt;<\/span><\/span>,\n  },\n];\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> routes;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><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>Here we declare two routes and the element to be rendered when the route is hit. Next, update the <code>src\/App.js<\/code> file to match the following:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-15\" 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> <span class=\"hljs-string\">\".\/App.css\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { useRoutes } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react-router-dom\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> routes <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/routes\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> Menu <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/components\/Menu\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { Col, Row } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"antd\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> App = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> router = useRoutes(routes);\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\">margin:<\/span> \"<span class=\"hljs-attr\">1<\/span>%\" }}&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Menu<\/span> \/&gt;<\/span>\n      <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\">textAlign:<\/span> \"<span class=\"hljs-attr\">center<\/span>\" }}&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Row<\/span> <span class=\"hljs-attr\">justify<\/span>=<span class=\"hljs-string\">\"center\"<\/span> <span class=\"hljs-attr\">align<\/span>=<span class=\"hljs-string\">\"middle\"<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">textAlign:<\/span> \"<span class=\"hljs-attr\">center<\/span>\" }}&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Col<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">width:<\/span> \"<span class=\"hljs-attr\">100<\/span>%\", <span class=\"hljs-attr\">margin:<\/span> \"<span class=\"hljs-attr\">2<\/span>%\" }}&gt;<\/span>{router}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Col<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Row<\/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\">div<\/span>&gt;<\/span><\/span>\n  );\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-15\"><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>Here, we render the menu and the router &#8211; which renders the relevant component based on the current route.<\/p>\n<p>Finally, update the <code>src\/index.js<\/code> to match the following:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-16\" 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<span class=\"hljs-keyword\">import<\/span> { BrowserRouter } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react-router-dom\"<\/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>));\n\nroot.render(\n  <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">React.StrictMode<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">BrowserRouter<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">App<\/span> \/&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">BrowserRouter<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">React.StrictMode<\/span>&gt;<\/span><\/span>\n);\n\nreportWebVitals();\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><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>With everything in place, run your 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<p>By default, the application will be available at <a href=\"http:\/\/localhost:3000\/\">http:\/\/localhost:3000\/<\/a>. The final result will look like the gif 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_1654537185171_CleanShot+2022-06-06+at+18.39.00.gif\" alt=\"\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"800\" height=\"392\"\/><\/p>\n<p>Find the complete project <a href=\"https:\/\/github.com\/ifeoma-imoh\/cloudinary-imagga-auto-tagging\">here<\/a> on GitHub.<\/p>\n<h2>Conclusion<\/h2>\n<p>In this article, we looked at how to take advantage of Imagga to automatically add tags to images uploaded to Cloudinary. Taking advantage of the flexibility of the Cloudinary API, we were able to build an application equipped with the tagging functionality and retrieve images based on tags &#8211; all without a backend.<\/p>\n<p>Cloudinary also offers even more robust features which can take our application further by allowing us to retrieve <strong>ALL<\/strong> the tags used to upload images, thereby giving our application even more control over the tags instead of being restricted to recently classified tags.<\/p>\n<p><strong>Resources you may find helpful:<\/strong><\/p>\n<ul>\n<li>\n<a href=\"https:\/\/cloudinary.com\/documentation\/imagga_auto_tagging_addon\">Documentation on Imagga Auto Tagging<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/cloudinary.com\/documentation\/node_integration\">Cloudinary Node.js SDK<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/cloudinary.com\/documentation\/react1_integration\">Documentation on Cloudinary React SDK (Legacy)<\/a>\n<\/li>\n<\/ul>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":27994,"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-27993","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>Add Tags to Images before Upload with Imagga<\/title>\n<meta name=\"description\" content=\"In this tutorial, we will use Cloudinary&#039;s Imagga Auto Tagging add-on to add tags to images that have been uploaded to our Cloudinary store. Additionally, we will render the images in a gallery, providing a list of tags that the user can click to filter the rendered 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\/add-tags-to-images-before-upload-with-imagga\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Add Tags to Images before Upload with Imagga\" \/>\n<meta property=\"og:description\" content=\"In this tutorial, we will use Cloudinary&#039;s Imagga Auto Tagging add-on to add tags to images that have been uploaded to our Cloudinary store. Additionally, we will render the images in a gallery, providing a list of tags that the user can click to filter the rendered images.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-06-09T09:06:22+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-03-23T18:38:21+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1681925733\/Web_Assets\/blog\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4-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\/add-tags-to-images-before-upload-with-imagga\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Add Tags to Images before Upload with Imagga\",\"datePublished\":\"2022-06-09T09:06:22+00:00\",\"dateModified\":\"2025-03-23T18:38:21+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/\"},\"wordCount\":8,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925733\/Web_Assets\/blog\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4.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\/add-tags-to-images-before-upload-with-imagga\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/\",\"name\":\"Add Tags to Images before Upload with Imagga\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925733\/Web_Assets\/blog\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4.png?_i=AA\",\"datePublished\":\"2022-06-09T09:06:22+00:00\",\"dateModified\":\"2025-03-23T18:38:21+00:00\",\"description\":\"In this tutorial, we will use Cloudinary's Imagga Auto Tagging add-on to add tags to images that have been uploaded to our Cloudinary store. Additionally, we will render the images in a gallery, providing a list of tags that the user can click to filter the rendered images.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925733\/Web_Assets\/blog\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925733\/Web_Assets\/blog\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4.png?_i=AA\",\"width\":1280,\"height\":720},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Add Tags to Images before Upload with Imagga\"}]},{\"@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":"Add Tags to Images before Upload with Imagga","description":"In this tutorial, we will use Cloudinary's Imagga Auto Tagging add-on to add tags to images that have been uploaded to our Cloudinary store. Additionally, we will render the images in a gallery, providing a list of tags that the user can click to filter the rendered 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\/add-tags-to-images-before-upload-with-imagga\/","og_locale":"en_US","og_type":"article","og_title":"Add Tags to Images before Upload with Imagga","og_description":"In this tutorial, we will use Cloudinary's Imagga Auto Tagging add-on to add tags to images that have been uploaded to our Cloudinary store. Additionally, we will render the images in a gallery, providing a list of tags that the user can click to filter the rendered images.","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/","og_site_name":"Cloudinary Blog","article_published_time":"2022-06-09T09:06:22+00:00","article_modified_time":"2025-03-23T18:38:21+00:00","og_image":[{"width":1280,"height":720,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1681925733\/Web_Assets\/blog\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4-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\/add-tags-to-images-before-upload-with-imagga\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/"},"author":{"name":"","@id":""},"headline":"Add Tags to Images before Upload with Imagga","datePublished":"2022-06-09T09:06:22+00:00","dateModified":"2025-03-23T18:38:21+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/"},"wordCount":8,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925733\/Web_Assets\/blog\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4.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\/add-tags-to-images-before-upload-with-imagga\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/","name":"Add Tags to Images before Upload with Imagga","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925733\/Web_Assets\/blog\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4.png?_i=AA","datePublished":"2022-06-09T09:06:22+00:00","dateModified":"2025-03-23T18:38:21+00:00","description":"In this tutorial, we will use Cloudinary's Imagga Auto Tagging add-on to add tags to images that have been uploaded to our Cloudinary store. Additionally, we will render the images in a gallery, providing a list of tags that the user can click to filter the rendered images.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925733\/Web_Assets\/blog\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925733\/Web_Assets\/blog\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4.png?_i=AA","width":1280,"height":720},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/add-tags-to-images-before-upload-with-imagga\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Add Tags to Images before Upload with Imagga"}]},{"@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\/v1681925733\/Web_Assets\/blog\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4\/48c209ec299970eb10d949e9def4d5bd44638103-1280x720-1_2799428ce4.png?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27993","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=27993"}],"version-history":[{"count":4,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27993\/revisions"}],"predecessor-version":[{"id":37242,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27993\/revisions\/37242"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/27994"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=27993"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=27993"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=27993"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}