{"id":27856,"date":"2022-05-03T15:18:38","date_gmt":"2022-05-03T15:18:38","guid":{"rendered":"http:\/\/image-optimization-with-cloudinary"},"modified":"2025-04-16T12:58:47","modified_gmt":"2025-04-16T19:58:47","slug":"image-optimization-with-cloudinary","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/","title":{"rendered":"Image Optimization with Cloudinary"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>With websites being accessed from many devices worldwide, the attention paid to image delivery could be the difference between high customer retention and a high website bounce rate. Special attention has to be paid to the nature of the devices available and tailoring the delivered image to each device to ensure maximum satisfaction. However, it doesn\u2019t stop there; we also need to consider the size of the image to be delivered and how this can be managed to provide a smooth user experience. This is what is referred to as image optimization.<\/p>\n<p><a href=\"https:\/\/cloudinary.com\/\">Cloudinary<\/a> has made a name for itself by not only providing a means of image storage but also providing fast CDN delivery, which helps to get resources to your users quickly. Additionally, Cloudinary automatically performs certain optimizations on transformed images and videos by default and it also provides features that enable you to optimize your media further to fit your needs.<\/p>\n<p>In this article, we\u2019ll look at how to optimize images for various screen sizes by building a simple photo album application with <a href=\"https:\/\/reactjs.org\/\">React<\/a>. This application will allow us to upload multiple images to our Cloudinary account and then view them in a grid.<\/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 account.<\/p>\n<p>Here is a <a href=\"https:\/\/codesandbox.io\/s\/image-optimization-with-cloudinary-ig4py3\">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\/image-optimization-with-cloudinary-ig4py3?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=\"image-optimization-with-cloudinary-ig4py3\"\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>Setting Up the Project<\/h2>\n<p>Create a React application using the following command:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">npx create-react-app image_optimization_demo\n<\/code><\/span><\/pre>\n<p>Next, let\u2019s add the project dependencies.<\/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\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<h2>Add Styling<\/h2>\n<p>Let\u2019s import the CSS for antd. Open your <code>src\/App.css<\/code> file 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\n<span class=\"hljs-selector-class\">.overlay<\/span> {\n    <span class=\"hljs-attribute\">position<\/span>: fixed;\n    <span class=\"hljs-attribute\">display<\/span>: none;\n    <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">100%<\/span>;\n    <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-number\">100%<\/span>;\n    <span class=\"hljs-attribute\">top<\/span>: <span class=\"hljs-number\">0<\/span>;\n    <span class=\"hljs-attribute\">left<\/span>: <span class=\"hljs-number\">0<\/span>;\n    <span class=\"hljs-attribute\">right<\/span>: <span class=\"hljs-number\">0<\/span>;\n    <span class=\"hljs-attribute\">bottom<\/span>: <span class=\"hljs-number\">0<\/span>;\n    <span class=\"hljs-attribute\">background-color<\/span>: <span class=\"hljs-built_in\">rgba<\/span>(<span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">0.5<\/span>);\n    <span class=\"hljs-attribute\">z-index<\/span>: <span class=\"hljs-number\">2<\/span>;\n    <span class=\"hljs-attribute\">cursor<\/span>: pointer;\n}\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<p>We also declare the styling for a class named <code>overlay<\/code>. This will be used to blur the screen when the user is uploading images.<\/p>\n<p>For the image grid, we\u2019ll also need some custom styling. Create a new file called <code>Grid.css<\/code> in the <code>src<\/code> folder and add the following to it:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-class\">.gallery<\/span> {\n    <span class=\"hljs-attribute\">display<\/span>: grid;\n    <span class=\"hljs-attribute\">grid-gap<\/span>: <span class=\"hljs-number\">10px<\/span>;\n    <span class=\"hljs-attribute\">grid-template-columns<\/span>: <span class=\"hljs-built_in\">repeat<\/span>(auto-fit, minmax(<span class=\"hljs-number\">150px<\/span>, <span class=\"hljs-number\">1<\/span>fr));\n}\n\n<span class=\"hljs-selector-class\">.gallery<\/span> <span class=\"hljs-selector-tag\">img<\/span> {\n    <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">100%<\/span>;\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\">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>For this tutorial, we will be sending images to Cloudinary via unsigned POST \u00a0requests. To do this, we need our account cloud name and an unsigned <a href=\"https:\/\/cloudinary.com\/documentation\/upload_presets\">upload preset<\/a>.<\/p>\n<h2>Create Helper Class for API Requests<\/h2>\n<p>To help with requests to the Cloudinary API, in your <code>src<\/code> folder, create a folder called <code>util<\/code>, and inside it, create a file called <code>api.js<\/code> and add the following to it:<\/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> axios <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"axios\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> getImages = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ successCallback }<\/span>) =&gt;<\/span> {\n  axios\n    .get(<span class=\"hljs-string\">`https:\/\/res.cloudinary.com\/YOURCLOUDNAME\/image\/list\/image_optimization_demo.json`<\/span>)\n    .then(<span class=\"hljs-function\">(<span class=\"hljs-params\">response<\/span>) =&gt;<\/span> successCallback(response.data.resources));\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> uploadFiles = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ selectedFiles, successCallback }<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> uploadResults = &#91;];\n  selectedFiles.forEach(<span class=\"hljs-function\">(<span class=\"hljs-params\">file<\/span>) =&gt;<\/span> {\n    uploadFile({\n      file,\n      <span class=\"hljs-attr\">successCallback<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">response<\/span>) =&gt;<\/span> {\n        uploadResults.push(response);\n        <span class=\"hljs-keyword\">if<\/span> (uploadResults.length === selectedFiles.length) {\n          successCallback(uploadResults);\n        }\n      },\n    });\n  });\n};\n\n<span class=\"hljs-keyword\">const<\/span> uploadFile = <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\/YOURCLOUDNAME\/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>, <span class=\"hljs-string\">\"YOURUPLOADPRESET\"<\/span>);\n  data.append(<span class=\"hljs-string\">\"tags\"<\/span>, <span class=\"hljs-string\">\"image_optimization_demo\"<\/span>);\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<\/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>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 the tag <code>image_optimization_demo<\/code>.<\/p>\n<p>To ensure that this feature is available on your Cloudinary account, you need to enable the \u00a0Resource list option. By default, the list delivery type is restricted. To enable it, open the <strong>Security settings<\/strong> 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_A1DDAD984DA0826D5292CEA08A9DCD06B2B4DA9D1A22D2ADC7A3AAC80A765B17_1638657976854_Screenshot+2021-12-04+at+23.45.51.png\" alt=\"https:\/\/paper-attachments.dropbox.com\/s_A1DDAD984DA0826D5292CEA08A9DCD06B2B4DA9D1A22D2ADC7A3AAC80A765B17_1638657976854_Screenshot+2021-12-04+at+23.45.51.png\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"2000\" height=\"701\"\/><\/p>\n<p>The <code>uploadFiles<\/code> function is used to upload the selected images to Cloudinary. It iterates through the list of selected images and, using the <code>uploadImage<\/code> function, uploads the image to Cloudinary, attaching the <code>image_optimization_demo<\/code> tag to the image. This will be used to filter the images in our store and only retrieve the album images when we want to populate the grid. A callback function is also provided, which will be executed once all the images have been uploaded. This callback function also receives the result of the upload process, which can be used elsewhere in the application.<\/p>\n<blockquote>\n<p>Update YOURCLOUDNAME and YOURUPLOADPRESET as specified in your Cloudinary account.<\/p>\n<\/blockquote>\n<h2>Creating the <code>ImageUpload<\/code> Component<\/h2>\n<p>Next, let\u2019s create a component to upload images to Cloudinary. In the <code>src<\/code> directory, create a new folder named <code>components<\/code>.<\/p>\n<p>In the <code>src\/components<\/code> directory, create a new file called <code>ImageUpload.js<\/code> and add the following to it:<\/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\">import<\/span> { Upload, Button, Card, Col, Row } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"antd\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { UploadOutlined } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@ant-design\/icons\"<\/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> { uploadFiles } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/util\/api\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> ImageUpload = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ onCompletion }<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> &#91;selectedFiles, setSelectedFiles] = useState(&#91;]);\n  <span class=\"hljs-keyword\">const<\/span> &#91;showSubmitButton, setShowSubmitButton] = useState(<span class=\"hljs-literal\">false<\/span>);\n  <span class=\"hljs-keyword\">const<\/span> &#91;isUploading, setIsUploading] = useState(<span class=\"hljs-literal\">false<\/span>);\n\n  useEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    setShowSubmitButton(selectedFiles.length &gt; <span class=\"hljs-number\">0<\/span>);\n  }, &#91;selectedFiles]);\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> beforeUpload = <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\">const<\/span> successCallback = <span class=\"hljs-function\">(<span class=\"hljs-params\">response<\/span>) =&gt;<\/span> {\n    setIsUploading(<span class=\"hljs-literal\">false<\/span>);\n    onCompletion(response);\n  };\n  \n  <span class=\"hljs-keyword\">const<\/span> handleSubmit = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    setIsUploading(<span class=\"hljs-literal\">true<\/span>);\n    uploadFiles({ selectedFiles, successCallback });\n  };\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>\n        <span class=\"hljs-attr\">position:<\/span> \"<span class=\"hljs-attr\">fixed<\/span>\",\n        <span class=\"hljs-attr\">top:<\/span> \"<span class=\"hljs-attr\">50<\/span>%\",\n        <span class=\"hljs-attr\">left:<\/span> \"<span class=\"hljs-attr\">50<\/span>%\",\n        <span class=\"hljs-attr\">transform:<\/span> \"<span class=\"hljs-attr\">translate<\/span>(<span class=\"hljs-attr\">-50<\/span>%, <span class=\"hljs-attr\">-50<\/span>%)\",\n      }}\n    &gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Upload<\/span>\n        <span class=\"hljs-attr\">onRemove<\/span>=<span class=\"hljs-string\">{removeFile}<\/span>\n        <span class=\"hljs-attr\">addFile<\/span>=<span class=\"hljs-string\">{addFile}<\/span>\n        <span class=\"hljs-attr\">beforeUpload<\/span>=<span class=\"hljs-string\">{beforeUpload}<\/span>\n        <span class=\"hljs-attr\">fileList<\/span>=<span class=\"hljs-string\">{selectedFiles}<\/span>\n        <span class=\"hljs-attr\">multiple<\/span>=<span class=\"hljs-string\">{true}<\/span>\n        <span class=\"hljs-attr\">accept<\/span>=<span class=\"hljs-string\">\"image\/*\"<\/span>\n      &gt;<\/span>\n        {!showSubmitButton &amp;&amp; <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Button<\/span>&gt;<\/span>Select files<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Button<\/span>&gt;<\/span>}\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Upload<\/span>&gt;<\/span>\n      {showSubmitButton &amp;&amp; (\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Row<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Col<\/span> <span class=\"hljs-attr\">offset<\/span>=<span class=\"hljs-string\">{9}<\/span> <span class=\"hljs-attr\">span<\/span>=<span class=\"hljs-string\">{6}<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Button<\/span>\n              <span class=\"hljs-attr\">icon<\/span>=<span class=\"hljs-string\">{<\/span>&lt;<span class=\"hljs-attr\">UploadOutlined<\/span> \/&gt;<\/span>}\n              type=\"primary\"\n              onClick={handleSubmit}\n              loading={isUploading}\n            &gt;\n              Upload\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Button<\/span>&gt;<\/span>\n          <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      )}\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-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>In this component, we declare three state variables &#8211; one to keep track of user file selection, another to determine whether or not the submit button should be rendered, and the last to determine whether or not files are being uploaded, and this will be used to show a loading animation on the submit button when the images are being uploaded.<\/p>\n<p>We use the <code>useEffect<\/code> hook to keep track of the number of files selected by the user. Once the user has selected some files, we render the <code>submit<\/code> button to allow the user to upload the current selection. However, if the user has not selected any file, the submit button is not rendered.<\/p>\n<p>The file selection in state is modified using the <code>addFile<\/code> and <code>removeFile<\/code> functions. These are used by the <code>Upload<\/code> component to update the list of files in state based on the user\u2019s action.<\/p>\n<p>The <code>beforeUpload<\/code> function is passed to the <code>Upload<\/code> component as a prop. It returns <code>false<\/code> to prevent the automatic upload of files by antd.<\/p>\n<p>The <code>handleSubmit<\/code> function is used as a handler for when the <code>Upload<\/code> button is clicked. This function sets the <code>isUploading<\/code> state variable to <code>true<\/code> and calls the <code>uploadFiles<\/code> function we declared earlier to initiate the upload process.<\/p>\n<p>Finally, we declare the JSX for the component, a button the user clicks to select the files they wish to upload, and a button (which is rendered conditionally depending on whether or not the user has selected any image). Using the <code>accept<\/code> prop for the <code>Upload<\/code> component, we restrict the possible selection of files to images.<\/p>\n<h2>Creating the <code>ImageGrid<\/code> Component<\/h2>\n<p>In the <code>src\/components<\/code> directory, create a new file called <code>ImageGrid.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> <span class=\"hljs-string\">\"..\/Grid.css\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> baseUrl = <span class=\"hljs-string\">\"https:\/\/res.cloudinary.com\/YOURCLOUDNAME\/image\/upload\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> generateSrcSet = <span class=\"hljs-function\">(<span class=\"hljs-params\">image<\/span>) =&gt;<\/span>\n  <span class=\"hljs-string\">`\n  <span class=\"hljs-subst\">${baseUrl}<\/span>\/w_400,h_300,c_scale\/v<span class=\"hljs-subst\">${image.version}<\/span>\/<span class=\"hljs-subst\">${image.public_id}<\/span>.<span class=\"hljs-subst\">${image.format}<\/span> 400w,\n  <span class=\"hljs-subst\">${baseUrl}<\/span>\/w_800,h_600,c_scale\/v<span class=\"hljs-subst\">${image.version}<\/span>\/<span class=\"hljs-subst\">${image.public_id}<\/span>.<span class=\"hljs-subst\">${image.format}<\/span> 800w,\n  <span class=\"hljs-subst\">${baseUrl}<\/span>\/w_1200,h_900,c_scale\/v<span class=\"hljs-subst\">${image.version}<\/span>\/<span class=\"hljs-subst\">${image.public_id}<\/span>.<span class=\"hljs-subst\">${image.format}<\/span> 1200w,\n  `<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> ImageGrid = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ images }<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"gallery\"<\/span>&gt;<\/span>\n      {images.map((image) =&gt; (\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span>\n          <span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{image.public_id}<\/span>\n          <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{<\/span>`${<span class=\"hljs-attr\">baseUrl<\/span>}\/<span class=\"hljs-attr\">w_400<\/span>,<span class=\"hljs-attr\">h_200<\/span>,<span class=\"hljs-attr\">c_scale<\/span>\/<span class=\"hljs-attr\">v<\/span>${<span class=\"hljs-attr\">image.version<\/span>}\/${<span class=\"hljs-attr\">image.public_id<\/span>}<span class=\"hljs-attr\">.<\/span>${<span class=\"hljs-attr\">image.format<\/span>}`}\n          <span class=\"hljs-attr\">srcSet<\/span>=<span class=\"hljs-string\">{generateSrcSet(image)}<\/span>\n          <span class=\"hljs-attr\">sizes<\/span>=<span class=\"hljs-string\">\"(max-width: 768px) 300px,(max-width: 992px) 600px, 900px\"<\/span>\n        \/&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> ImageGrid;\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>ImageGrid<\/code> component takes an array of <a href=\"https:\/\/cloudinary.com\/products\/image\">Cloudinary image<\/a> resources as a prop, iterates through the array, and renders each image.<\/p>\n<p>To ensure that the images are optimized for screen size, in addition to providing a default image source (in the <code>src<\/code> prop), we declare two additional props &#8211; <code>srcSet<\/code> and <code>sizes<\/code>. Using these, the image source and size are set based on the screen width of the user\u2019s device.<\/p>\n<p>The string value of the <code>srcSet<\/code> prop is generated using the <code>generateSrcSet<\/code> function. The <code>generateSrcSet<\/code> function takes advantage of Cloudinary\u2019s URL transformation feature to generate three versions of the same image on the fly.<\/p>\n<blockquote>\n<p>Update YOURCLOUDNAME in the baseUrl with your cloud name.<\/p>\n<\/blockquote>\n<h2>Putting it Together<\/h2>\n<p>Finally, modify your <code>src\/App.js<\/code> file to match the following:<\/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> <span class=\"hljs-string\">\".\/App.css\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { UploadOutlined } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@ant-design\/icons\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { message, Menu } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"antd\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> ImageGrid <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/components\/ImageGrid\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> ImageUpload <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/components\/ImageUpload\"<\/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> { getImages } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/util\/api\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> items = &#91;\n  {\n    <span class=\"hljs-attr\">label<\/span>: <span class=\"hljs-string\">\"Upload Image\"<\/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\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> &#91;images, setImages] = useState(&#91;]);\n  <span class=\"hljs-keyword\">const<\/span> &#91;showImageUpload, setShowImageUpload] = useState(<span class=\"hljs-literal\">true<\/span>);\n\n  useEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    getImages({\n      <span class=\"hljs-attr\">successCallback<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">response<\/span>) =&gt;<\/span> {\n        setImages(response);\n        setShowImageUpload(response.length === <span class=\"hljs-number\">0<\/span>);\n      },\n    });\n  }, &#91;]);\n\n  <span class=\"hljs-keyword\">const<\/span> onCompletion = <span class=\"hljs-function\">(<span class=\"hljs-params\">response<\/span>) =&gt;<\/span> {\n    setImages(response);\n    message.success(<span class=\"hljs-string\">\"Images uploaded successfully\"<\/span>);\n    setShowImageUpload(<span class=\"hljs-literal\">false<\/span>);\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\">margin:<\/span> \"<span class=\"hljs-attr\">1<\/span>%\" }}&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>\n        <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"overlay\"<\/span>\n        <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span>\n          <span class=\"hljs-attr\">display:<\/span> `${<span class=\"hljs-attr\">showImageUpload<\/span> ? \"<span class=\"hljs-attr\">block<\/span>\" <span class=\"hljs-attr\">:<\/span> \"<span class=\"hljs-attr\">none<\/span>\"}`,\n        }}\n      &gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ImageUpload<\/span> <span class=\"hljs-attr\">onCompletion<\/span>=<span class=\"hljs-string\">{onCompletion}<\/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\">Menu<\/span>\n        <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{()<\/span> =&gt;<\/span> {\n          setShowImageUpload(true);\n        }}\n        mode=\"horizontal\"\n        items={items}\n        style={{ marginBottom: \"10px\" }}\n      \/&gt;\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ImageGrid<\/span> <span class=\"hljs-attr\">images<\/span>=<span class=\"hljs-string\">{images}<\/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-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>On page load, we retrieve the images from Cloudinary using the <code>getImages<\/code> function declared earlier. If the result is an empty array, then we render the <code>ImageUpload<\/code> component. If not, we pass the resources to the <code>ImageGrid<\/code> component to be rendered accordingly.<\/p>\n<p>Find the complete project <a href=\"https:\/\/github.com\/ifeoma-imoh\/image-optimization-cloudinary\">here<\/a> on GitHub.<\/p>\n<h2>Conclusion<\/h2>\n<p>In this article, we created a basic photo album application and took advantage of the <code>srcset<\/code> and <code>sizes<\/code> HTML attribute to optimize the size of images rendered based on the user\u2019s device screen size.<\/p>\n<p>In the generation of the <code>srcSet<\/code> string, we saw one of the advantages of using Cloudinary. We were able to use the URL transformation feature to generate images of different sizes without duplicating and modifying the images for each screen size manually. However, it doesn\u2019t stop there, as Cloudinary provides additional optimizations that make for a seamless experience.<\/p>\n<p>By default, all metadata is stripped from the images being returned. This reduces the image size and the time taken to download the image. Also, by providing the images via CDN, the waiting time to retrieve the images is reduced, thus making for a quick retrieval and download process.<\/p>\n<p>** Resources you may find helpful: **<\/p>\n<ul>\n<li>\n<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Learn\/HTML\/Multimedia_and_embedding\/Responsive_images\">MDN documentation on responsive images<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/cloudinary.com\/documentation\/transformation_reference\">Cloudinary Transformation URL API reference<\/a>\n<\/li>\n<\/ul>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":27857,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[381,134,145,370,246,371],"class_list":["post-27856","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-imagecdn","tag-guest-post","tag-html5","tag-image","tag-react","tag-under-review"],"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>Image Optimization with Cloudinary<\/title>\n<meta name=\"description\" content=\"In this article, we&#039;ll look at how to optimize images for various screen sizes using the srcSet and sizes HTML attributes by building a simple photo album application with React. We will also take advantage of Cloudinary&#039;s URL transformation feature to automatically generate images of different sizes without duplicating or modifying the images for each screen size.\" \/>\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\/image-optimization-with-cloudinary\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Image Optimization with Cloudinary\" \/>\n<meta property=\"og:description\" content=\"In this article, we&#039;ll look at how to optimize images for various screen sizes using the srcSet and sizes HTML attributes by building a simple photo album application with React. We will also take advantage of Cloudinary&#039;s URL transformation feature to automatically generate images of different sizes without duplicating or modifying the images for each screen size.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-05-03T15:18:38+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-04-16T19:58:47+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1681926089\/Web_Assets\/blog\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e-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\/image-optimization-with-cloudinary\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Image Optimization with Cloudinary\",\"datePublished\":\"2022-05-03T15:18:38+00:00\",\"dateModified\":\"2025-04-16T19:58:47+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/\"},\"wordCount\":4,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926089\/Web_Assets\/blog\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e.png?_i=AA\",\"keywords\":[\"(Image)CDN\",\"Guest Post\",\"HTML5\",\"Image\",\"React\",\"Under Review\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2022\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/\",\"name\":\"Image Optimization with Cloudinary\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926089\/Web_Assets\/blog\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e.png?_i=AA\",\"datePublished\":\"2022-05-03T15:18:38+00:00\",\"dateModified\":\"2025-04-16T19:58:47+00:00\",\"description\":\"In this article, we'll look at how to optimize images for various screen sizes using the srcSet and sizes HTML attributes by building a simple photo album application with React. We will also take advantage of Cloudinary's URL transformation feature to automatically generate images of different sizes without duplicating or modifying the images for each screen size.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926089\/Web_Assets\/blog\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926089\/Web_Assets\/blog\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e.png?_i=AA\",\"width\":1280,\"height\":720},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Image Optimization with Cloudinary\"}]},{\"@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":"Image Optimization with Cloudinary","description":"In this article, we'll look at how to optimize images for various screen sizes using the srcSet and sizes HTML attributes by building a simple photo album application with React. We will also take advantage of Cloudinary's URL transformation feature to automatically generate images of different sizes without duplicating or modifying the images for each screen size.","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\/image-optimization-with-cloudinary\/","og_locale":"en_US","og_type":"article","og_title":"Image Optimization with Cloudinary","og_description":"In this article, we'll look at how to optimize images for various screen sizes using the srcSet and sizes HTML attributes by building a simple photo album application with React. We will also take advantage of Cloudinary's URL transformation feature to automatically generate images of different sizes without duplicating or modifying the images for each screen size.","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/","og_site_name":"Cloudinary Blog","article_published_time":"2022-05-03T15:18:38+00:00","article_modified_time":"2025-04-16T19:58:47+00:00","og_image":[{"width":1280,"height":720,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1681926089\/Web_Assets\/blog\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e-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\/image-optimization-with-cloudinary\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/"},"author":{"name":"","@id":""},"headline":"Image Optimization with Cloudinary","datePublished":"2022-05-03T15:18:38+00:00","dateModified":"2025-04-16T19:58:47+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/"},"wordCount":4,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926089\/Web_Assets\/blog\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e.png?_i=AA","keywords":["(Image)CDN","Guest Post","HTML5","Image","React","Under Review"],"inLanguage":"en-US","copyrightYear":"2022","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/","name":"Image Optimization with Cloudinary","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926089\/Web_Assets\/blog\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e.png?_i=AA","datePublished":"2022-05-03T15:18:38+00:00","dateModified":"2025-04-16T19:58:47+00:00","description":"In this article, we'll look at how to optimize images for various screen sizes using the srcSet and sizes HTML attributes by building a simple photo album application with React. We will also take advantage of Cloudinary's URL transformation feature to automatically generate images of different sizes without duplicating or modifying the images for each screen size.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926089\/Web_Assets\/blog\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681926089\/Web_Assets\/blog\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e.png?_i=AA","width":1280,"height":720},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/image-optimization-with-cloudinary\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Image Optimization with Cloudinary"}]},{"@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\/v1681926089\/Web_Assets\/blog\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e\/ea3d89cb34fd09734cb154f171b7b737ac0635bc-1280x720-1_27857cb41e.png?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27856","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=27856"}],"version-history":[{"count":1,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27856\/revisions"}],"predecessor-version":[{"id":37441,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/27856\/revisions\/37441"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/27857"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=27856"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=27856"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=27856"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}