{"id":35703,"date":"2024-09-17T07:00:00","date_gmt":"2024-09-17T14:00:00","guid":{"rendered":"https:\/\/cloudinary.com\/blog\/?p=35703"},"modified":"2024-09-17T11:52:04","modified_gmt":"2024-09-17T18:52:04","slug":"building-ai-image-manager-dall-e-cloudinary","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary","title":{"rendered":"Building an AI Image Manager With DALL-E and Cloudinary"},"content":{"rendered":"\n<p>Artificial intelligence (AI) has transformed image creation, enabling us to design landscapes or dreamlike portraits with a few words. This newfound creativity comes with the challenge of managing a constantly growing library of images, as traditional cloud storages lack the features needed for efficient delivery and organization. There\u2019s a need for a viable storage option to minimize development time while keeping the image library organized and easily accessible for technical and non-technical users.<\/p>\n\n\n\n<p>This blog post shows how to build an AI image manager that leverages the power of AI image generation and Cloudinary&#8217;s storage capabilities. The complete source code of this project is on <a target=\"_blank\" href=\"https:\/\/github.com\/ugwutotheeshoes\/dall-e-3-cloudinary-nextjs\" rel=\"noreferrer noopener\">Git<\/a><a target=\"_blank\" href=\"https:\/\/github.com\/ugwutotheeshoes\/dall-e-3-cloudinary-nextjs\" rel=\"noreferrer noopener\">H<\/a><a target=\"_blank\" href=\"https:\/\/github.com\/ugwutotheeshoes\/dall-e-3-cloudinary-nextjs\" rel=\"noreferrer noopener\">ub<\/a>.<\/p>\n\n\n\n<p>To get the most out of this blog post, you\u2019ll need:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A<a href=\"https:\/\/cloudinary.com\/\" target=\"_blank\" rel=\"noreferrer noopener\"> <\/a>free <a href=\"https:\/\/cloudinary.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Cloudinary account<\/a>.<\/li>\n\n\n\n<li><a href=\"https:\/\/chatgpt.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">A ChatGPT Plus <\/a><a href=\"https:\/\/chatgpt.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">a<\/a><a href=\"https:\/\/chatgpt.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">ccount<\/a> to get access to DALL-E 3.<\/li>\n\n\n\n<li>A basic understanding of Next.js and Typescript.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Project Setup and Installation<\/h2>\n\n\n\n<p>With your Cloudinary account ready, bootstrap a Next.js app with the command below:\u00a0<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">code<\/span>&gt;<\/span>npx create-next-app@latest<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">code<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You\u2019ll receive prompts to set up the app. Select Typescript, Tailwind CSS, and App Router, as they\u2019re needed. Then, install the necessary dependencies with the following command:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">code<\/span>&gt;<\/span>npm install cloudinary react-copy-to-clipboard @types\/react-copy-to-clipboard react-icons openai<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">code<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The packages installed perform the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>cloudinary<\/code>. Provides an interface to interact with Cloudinary.<\/li>\n\n\n\n<li><code>react-copy-to-clipboard<\/code>. Provides a React component that enables users to copy texts with a single click.<\/li>\n\n\n\n<li><code>@types\/react-copy-to=clipboard<\/code>. This is a type definition file for the react-copy-to-clipboard package.<\/li>\n\n\n\n<li><code>react-icons<\/code>. Offers a collection of icons (like Font Awesome or Material UI) that can be easily integrated into your application.<\/li>\n\n\n\n<li><code>openai<\/code>. Interacts with the OpenAI API.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Configure OpenAI API<\/strong><\/h2>\n\n\n\n<p>Once done, go to the next.config.js file in the project&#8217;s root directory and add this code snippet to allow Next.js to display images generated from DALL-E 3:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/** <span class=\"hljs-doctag\">@type <span class=\"hljs-type\">{import('next').NextConfig}<\/span> <\/span>*\/<\/span>\n\n<span class=\"hljs-keyword\">const<\/span> nextConfig = {\n\n\u00a0 <span class=\"hljs-attr\">images<\/span>: {\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">formats<\/span>: &#91;<span class=\"hljs-string\">\"image\/avif\"<\/span>, <span class=\"hljs-string\">\"image\/webp\"<\/span>],\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">remotePatterns<\/span>: &#91;\n\n\u00a0 \u00a0 \u00a0 {\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">protocol<\/span>: <span class=\"hljs-string\">\"https\"<\/span>,\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">hostname<\/span>: <span class=\"hljs-string\">\"oaidalleapiprodscus.blob.core.windows.net\"<\/span>,\n\n\u00a0 \u00a0 \u00a0 },\n\n\u00a0 \u00a0 ],\n\n\u00a0 },\n\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> nextConfig;<\/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\n\n<p>This prioritizes AVIF and WEBP formats for image optimization. It also allows Next.js to display images served from the Azure Blob Storage endpoint that OpenAI&#8217;s API uses.<\/p>\n\n\n\n<p>Running <code>npm run dev<\/code> should render your project at <a href=\"https:\/\/localhost:3000\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/localhost:3000\/<\/a> in your browser.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Get Cloudinary Account and OpenAI API Credentials<\/h2>\n\n\n\n<p>After you create a Cloudinary account, access the dashboard to find all the required credentials for this project. Copy and store them somewhere safe.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img width=\"1024\" height=\"302\" data-public-id=\"Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-1\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-1.png\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_1024,h_302,c_scale\/f_auto,q_auto\/v1726598986\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-1\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-1.png?_i=AA\" alt=\"\" class=\"wp-post-35703 wp-image-35704\" data-format=\"png\" data-transformations=\"f_auto,q_auto\" data-version=\"1726598986\" data-seo=\"1\" srcset=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598986\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-1\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-1.png?_i=AA 1600w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598986\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-1\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-1.png?_i=AA 300w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598986\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-1\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-1.png?_i=AA 768w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598986\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-1\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-1.png?_i=AA 1024w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598986\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-1\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-1.png?_i=AA 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Cloudinary Dashboard<\/figcaption><\/figure>\n\n\n\n<p>DALL-E 3 is an AI image generation model by OpenAI that generates realistic images based on text descriptions. DALL-E 3 is currently unavailable for free public use through the OpenAI API, as access is restricted to ChatGPT Plus, Team, and Enterprise customers.<\/p>\n\n\n\n<p>Next, navigate to the <a href=\"https:\/\/beta.openai.com\/account\/api-keys\" target=\"_blank\" rel=\"noreferrer noopener\">OpenAI API key page<\/a> and create a new key, as shown below. The key allows us to interact with DALL-E 3. Also copy and store these somewhere safe.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img width=\"1024\" height=\"511\" data-public-id=\"Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-2\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-2.png\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_1024,h_511,c_scale\/f_auto,q_auto\/v1726598979\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-2\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-2.png?_i=AA\" alt=\"\" class=\"wp-post-35703 wp-image-35705\" data-format=\"png\" data-transformations=\"f_auto,q_auto\" data-version=\"1726598979\" data-seo=\"1\" srcset=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598979\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-2\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-2.png?_i=AA 1600w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598979\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-2\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-2.png?_i=AA 300w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598979\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-2\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-2.png?_i=AA 768w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598979\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-2\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-2.png?_i=AA 1024w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598979\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-2\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-2.png?_i=AA 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Create an API Key<\/figcaption><\/figure>\n\n\n\n<p>Next, create a <code>.env.local<\/code> file in your project&#8217;s root folder to store the Open API key and Cloudinary credentials.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">\/\/ .env.local\n\nNEXT_PUBLIC_OPENAI_API_KEY=<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">openai_api_key<\/span>&gt;<\/span>\n\nNEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">cloudinary_api_key<\/span>&gt;<\/span>\n\nNEXT_PUBLIC_CLOUDINARY_API_KEY=<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">cloudinary_api_secret<\/span>&gt;<\/span>\n\nCLOUDINARY_API_SECRET=<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">cloudinary_cloud_name<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<div class='c-callout  c-callout--inline-title c-callout--note'><strong class='c-callout__title'>Note:<\/strong> <p>Never share your credentials publicly.<\/p>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">Build the App Functionality<\/h2>\n\n\n\n<p>The app\u2019s main functionality is to generate, store, and manage AI-generated images. For the image generation, the app component accepts a user prompt and displays the generated image based on the prompt. To do this, navigate to the src\/app\/pages file, create an input to accept the user prompt, and include a button to send the prompt in a request to OpenAI API.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ src\/app\/pages<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> { FaRegPaperPlane } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react-icons\/fa\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Home<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{ \u00a0\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> &#91;value, setValue] = useState&lt;string&gt;(<span class=\"hljs-string\">\"\"<\/span>);\n\n<span class=\"hljs-keyword\">return<\/span> ( \u00a0 \u00a0\n\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex min-h-screen flex-col items-center justify-between p-10\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"relative text-xl font-semibold capitalize \"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 AI image manager\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex items-center justify-between\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">placeholder<\/span>=<span class=\"hljs-string\">\"Enter an image prompt\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"value\"<\/span>\u00a0\n\n<span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> { setValue(e.target.value) }} className=\"bg-gray-100 placeholder:text-gray-400 disabled:cursor-not-allowed border border-gray-500 text-gray-900 text-sm rounded-lg block p-3.5 mr-2 w-&#91;600px]\" required \/&gt;\n\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"text-blue-700 relative right-&#91;3.5rem] font-medium p-5 rounded-lg text-sm transition-all sm:w-auto px-5 py-2.5 text-center\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">FaRegPaperPlane<\/span> \/&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span><\/span>\n\n\u00a0 )\n\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>After applying the necessary configurations, the app should look like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img width=\"1024\" height=\"511\" data-public-id=\"Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-3\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-3.png\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_1024,h_511,c_scale\/f_auto,q_auto\/v1726598976\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-3\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-3.png?_i=AA\" alt=\"\" class=\"wp-post-35703 wp-image-35706\" data-format=\"png\" data-transformations=\"f_auto,q_auto\" data-version=\"1726598976\" data-seo=\"1\" srcset=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598976\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-3\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-3.png?_i=AA 1600w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598976\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-3\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-3.png?_i=AA 300w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598976\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-3\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-3.png?_i=AA 768w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598976\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-3\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-3.png?_i=AA 1024w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598976\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-3\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-3.png?_i=AA 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Also, create a Loader component file, <code>src\/components\/Loader.tsx<\/code>, to give the user visual feedback on requests undergoing a process.<\/p>\n\n\n<pre class=\"wp-block-code\" 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-comment\">\/\/ src\/components\/Loader.tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> React <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'react'<\/span>\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Loader<\/span>(<span class=\"hljs-params\">{ size = <span class=\"hljs-number\">20<\/span> }<\/span>) <\/span>{\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> (\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex justify-center items-center\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"spinner\"<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">width:<\/span> `${<span class=\"hljs-attr\">size<\/span>}<span class=\"hljs-attr\">px<\/span>`, <span class=\"hljs-attr\">height:<\/span> `${<span class=\"hljs-attr\">size<\/span>}<span class=\"hljs-attr\">px<\/span>` }}&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n\n\u00a0 \u00a0 \u00a0 );\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\n\n<p>Then, navigate to the global.css file and add the Loader component\u2019s CSS style.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/src\/app\/global.css<\/span>\n\n@keyframes spinner {\n\n\u00a0 <span class=\"hljs-number\">0<\/span>% {\n\n\u00a0 \u00a0 transform: rotate(<span class=\"hljs-number\">0<\/span>deg);\n\n\u00a0 }\n\n\u00a0 <span class=\"hljs-number\">100<\/span>% {\n\n\u00a0 \u00a0 transform: rotate(<span class=\"hljs-number\">360<\/span>deg);\n\n\u00a0 }\n\n}\n\n.spinner {\n\n\u00a0 border: <span class=\"hljs-number\">4<\/span>px solid <span class=\"hljs-comment\">#f3f3f3;<\/span>\n\n\u00a0 border-top-color: <span class=\"hljs-comment\">#3498db; \/* Adjust color as desired *\/<\/span>\n\n\u00a0 border-radius: <span class=\"hljs-number\">50<\/span>%;\n\n\u00a0 animation: spin <span class=\"hljs-number\">1<\/span>s linear infinite;\n\n}\n\n@keyframes spin {\n\n\u00a0 <span class=\"hljs-number\">0<\/span>% { transform: rotate(<span class=\"hljs-number\">0<\/span>deg); }\n\n\u00a0 <span class=\"hljs-number\">100<\/span>% { transform: rotate(<span class=\"hljs-number\">360<\/span>deg); }\n\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Send User Prompts to OpenAI API<\/h2>\n\n\n\n<p>In this step, you&#8217;ll use Next.js\u2019 API routes to send the user prompt as a POST request parameter to OpenAI\u2019s API. The response, containing the generated image URL based on the prompt, will then be retrieved and processed.<\/p>\n\n\n\n<p>First, you\u2019ll send the text value to the API route, <code>\/api\/dalle3<\/code>. Create a <code>pages\/api<\/code> folder and add a file named <code>dalle3.ts<\/code>. This file will act as the API endpoint for your POST request. Then, add the code snippet below in the <code>dalle3.ts<\/code> file:<\/p>\n\n\n<pre class=\"wp-block-code\" 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-comment\">\/\/src\/pages\/api\/dalle3.ts<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> type { NextApiRequest, NextApiResponse } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'next'<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> OpenAI <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"openai\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> openai = <span class=\"hljs-keyword\">new<\/span> OpenAI({ <span class=\"hljs-attr\">apiKey<\/span>: process.env.NEXT_PUBLIC_DALL_E_3_API_KEY });\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handler<\/span>(<span class=\"hljs-params\">req: NextApiRequest, res: NextApiResponse<\/span>) <\/span>{\n\n\u00a0 <span class=\"hljs-keyword\">try<\/span> {\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> { prompt } = req.body; <span class=\"hljs-comment\">\/\/ Extract the prompt from the request body<\/span>\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> uploadResponse = <span class=\"hljs-keyword\">await<\/span> openai.images.generate({\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">model<\/span>: <span class=\"hljs-string\">\"dall-e-3\"<\/span>,\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">prompt<\/span>: prompt,\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">n<\/span>: <span class=\"hljs-number\">1<\/span>,\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">size<\/span>: <span class=\"hljs-string\">\"1024x1024\"<\/span>,\n\n\u00a0 \u00a0 });\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> image_url = uploadResponse.data&#91;<span class=\"hljs-number\">0<\/span>].url;\n\n\u00a0 \u00a0 res.status(<span class=\"hljs-number\">200<\/span>).json({ image_url });\n\n\u00a0 } <span class=\"hljs-keyword\">catch<\/span> (error: any) {\n\n\u00a0 \u00a0 <span class=\"hljs-built_in\">console<\/span>.error(error);\n\n\u00a0 \u00a0 res.status(<span class=\"hljs-number\">500<\/span>).json({ <span class=\"hljs-attr\">message<\/span>: error.message });\n\n\u00a0 }\n\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Let\u2019s break down the actions taken in each section of the code snippet above:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Lines<\/strong> <strong>1-3<\/strong><strong>.<\/strong> Imports the necessary components from Next.js to handle the request and response structure and create a new client specifically for interacting with the OpenAI API.<\/li>\n\n\n\n<li><strong>Line 6<\/strong><strong>.<\/strong> Extracts the user&#8217;s prompt (text value) from the data sent in the request.<\/li>\n\n\n\n<li><strong>Lines 7-12. <\/strong>This section makes an asynchronous call to the <code>OpenAI.images.generate<\/code> method. It instructs the method to use Dall-E 3, takes the extracted prompt as the image basis, creates a single image, and sets the size to 1024&#215;1024 pixels.<\/li>\n\n\n\n<li><strong>Lines 13-14<\/strong><strong>.<\/strong> If successful, it extracts the image URL from the response and sends a JSON object with the URL.<\/li>\n\n\n\n<li><strong>Lines 15-17<\/strong><strong>.<\/strong> It includes an error catch block to handle potential API communication issues.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Display the Generated Image<\/h2>\n\n\n\n<p>Now, you\u2019ll send the user&#8217;s prompt to your server-side endpoint. Create a function called <code>handlePrompt<\/code> within the <code>src\/app\/pages<\/code> file, then link it to the app\u2019s button. Inside this function, you\u2019ll convert the value stored in the state variable (containing the prompt) into a JSON object. This JSON object will then become the body of the request sent to the server.<\/p>\n\n\n<pre class=\"wp-block-code\" 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-comment\">\/\/ src\/app\/pages<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> { FaRegPaperPlane } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react-icons\/fa\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Home<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> &#91;url, setUrl] = useState&lt;string&gt;(<span class=\"hljs-string\">\"\"<\/span>);\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> &#91;loading, setLoading] = useState&lt;boolean | <span class=\"hljs-literal\">undefined<\/span>&gt;(<span class=\"hljs-literal\">false<\/span>); \u00a0\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> &#91;value, setValue] = useState&lt;string&gt;(<span class=\"hljs-string\">\"\"<\/span>);\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> handlePrompt = <span class=\"hljs-keyword\">async<\/span> (e: any) =&gt; {\n\n\u00a0 \u00a0 e.preventDefault()\n\n\u00a0 \u00a0 setLoading(<span class=\"hljs-literal\">true<\/span>)\n\n\u00a0 \u00a0 setUrl(<span class=\"hljs-string\">\"\"<\/span>)\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">try<\/span> {\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> fetch(<span class=\"hljs-string\">'\/api\/dalle3'<\/span>, {\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">'POST'<\/span>,\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">headers<\/span>: { <span class=\"hljs-string\">'Content-Type'<\/span>: <span class=\"hljs-string\">'application\/json'<\/span> },\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">body<\/span>: <span class=\"hljs-built_in\">JSON<\/span>.stringify({ <span class=\"hljs-attr\">prompt<\/span>: value }), <span class=\"hljs-comment\">\/\/ send the string as a JSON object<\/span>\n\n\u00a0 \u00a0 \u00a0 });\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-comment\">\/\/ Handle success, such as updating UI or showing a success message<\/span>\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">if<\/span> (response.ok) {\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> data = <span class=\"hljs-keyword\">await<\/span> response.json();\n\n\u00a0 \u00a0 \u00a0 \u00a0 setUrl(data.image_url)\n\n\u00a0 \u00a0 \u00a0 }\n\n\u00a0 \u00a0 } <span class=\"hljs-keyword\">catch<\/span> (error) {\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-comment\">\/\/ Handle network errors or other exceptions<\/span>\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\">'Error uploading file:'<\/span>, error);\n\n\u00a0 \u00a0 }\n\n\u00a0 };\n\n<span class=\"hljs-keyword\">return<\/span> ( \u00a0 \u00a0\n\n\u00a0 \u00a0 <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex min-h-screen flex-col items-center justify-between p-10\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 ... \/\/ App Title\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex items-center justify-between\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">placeholder<\/span>=<span class=\"hljs-string\">\"Enter an image prompt\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"value\"<\/span>\u00a0\n\n<span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> { setValue(e.target.value) }} className=\"bg-gray-100 placeholder:text-gray-400 disabled:cursor-not-allowed border border-gray-500 text-gray-900 text-sm rounded-lg block p-3.5 mr-2 w-&#91;600px]\" required \/&gt;\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"text-blue-700 relative right-&#91;3.5rem] font-medium p-5 rounded-lg text-sm transition-all sm:w-auto px-5 py-2.5 text-center\"<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{handlePrompt}<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">FaRegPaperPlane<\/span> \/&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span><\/span>\n\n\u00a0 )\n\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\n\n<p>To handle the OpenAI API response, the application state was updated with the generated image URL by storing the URL in a state variable named <code>url<\/code>.\u00a0<\/p>\n\n\n\n<p>Next, you&#8217;ll display the generated image from OpenAI API only when the server successfully processes the request. To achieve this, you&#8217;ll conditionally render the image based on the value of the <code>url<\/code> state variable. If the <code>url<\/code> has a value (meaning the image retrieval was successful), you&#8217;ll display the image and hide the loading indicator using the <code>onLoadingComplete<\/code> prop.<\/p>\n\n\n<pre class=\"wp-block-code\" 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-comment\">\/\/ src\/app\/pages<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> Image <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/image\"<\/span>;\n\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\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Home<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> &#91;url, setUrl] = useState&lt;string&gt;(<span class=\"hljs-string\">\"\"<\/span>);\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> &#91;loading, setLoading] = useState&lt;boolean | <span class=\"hljs-literal\">undefined<\/span>&gt;(<span class=\"hljs-literal\">false<\/span>);\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> &#91;value, setValue] = useState&lt;string&gt;(<span class=\"hljs-string\">\"\"<\/span>);\u00a0\n\n<span class=\"hljs-keyword\">return<\/span> ( \u00a0 \u00a0\n\n\u00a0 <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex min-h-screen flex-col items-center justify-between p-10\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 ... \/\/App Title\n\n\u00a0 \u00a0 \u00a0 \u00a0{url &amp;&amp;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex flex-col items-center justify-center\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Image<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{url}<\/span> <span class=\"hljs-attr\">onLoadingComplete<\/span>=<span class=\"hljs-string\">{()<\/span> =&gt;<\/span> setLoading(false)} width={500} height={500} alt=\"ai image\" \/&gt;\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0}\n\n\u00a0 \u00a0 \u00a0 ... \/\/ prompt input and button\n\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span><\/span>\n\n\u00a0 )\n\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The app should look like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img width=\"1024\" height=\"511\" data-public-id=\"Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-4\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-4.png\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_1024,h_511,c_scale\/f_auto,q_auto\/v1726598972\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-4\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-4.png?_i=AA\" alt=\"\" class=\"wp-post-35703 wp-image-35707\" data-format=\"png\" data-transformations=\"f_auto,q_auto\" data-version=\"1726598972\" data-seo=\"1\" srcset=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598972\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-4\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-4.png?_i=AA 1600w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598972\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-4\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-4.png?_i=AA 300w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598972\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-4\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-4.png?_i=AA 768w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598972\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-4\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-4.png?_i=AA 1024w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598972\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-4\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-4.png?_i=AA 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Store Image in Cloudinary and Browser Storage<\/h2>\n\n\n\n<p>Once you display the generated image, you can upload it to Cloudinary and get the upload URL. To do this, you\u2019ll create a new file named <code>cloudinary.ts<\/code> inside the <code>pages\/api<\/code> folder. This file will act as an API route for your upload request.<\/p>\n\n\n<pre class=\"wp-block-code\" 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-comment\">\/\/ pages\/api\/cloudinary.ts<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> type { NextApiRequest, NextApiResponse } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'next'<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { v2 <span class=\"hljs-keyword\">as<\/span> cloudinary } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'cloudinary'<\/span>;\n\ncloudinary.config({\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">cloud_name<\/span>: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">api_key<\/span>: process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY,\n\n\u00a0 \u00a0 <span class=\"hljs-attr\">api_secret<\/span>: process.env.CLOUDINARY_API_SECRET,\n\n});\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handler<\/span>(<span class=\"hljs-params\">req: NextApiRequest, res: NextApiResponse<\/span>) <\/span>{\n\n\u00a0 \u00a0 <span class=\"hljs-keyword\">try<\/span> {\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> image_url = req.body.url; <span class=\"hljs-comment\">\/\/ Extract the image URL from the request body<\/span>\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> timestamp = req.body.value \u00a0+ <span class=\"hljs-string\">' '<\/span> + <span class=\"hljs-built_in\">Math<\/span>.floor((<span class=\"hljs-built_in\">Math<\/span>.random() * <span class=\"hljs-number\">100<\/span>) + <span class=\"hljs-number\">1<\/span>);\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> trimmedString = timestamp.trim();\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> publicId = trimmedString.replace(<span class=\"hljs-regexp\">\/\\s+\/g<\/span>, <span class=\"hljs-string\">'-'<\/span>);\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> cloudinary.uploader.upload(image_url, {\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">transformation<\/span>: &#91;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 {<span class=\"hljs-attr\">border<\/span>: <span class=\"hljs-string\">\"10px_solid_blue\"<\/span>},\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 {<span class=\"hljs-attr\">radius<\/span>: <span class=\"hljs-number\">50<\/span>},\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 {<span class=\"hljs-attr\">color<\/span>: <span class=\"hljs-string\">\"#FFFFFF69\"<\/span>, <span class=\"hljs-attr\">overlay<\/span>: {<span class=\"hljs-attr\">font_family<\/span>: <span class=\"hljs-string\">\"Arial\"<\/span>, <span class=\"hljs-attr\">font_size<\/span>: <span class=\"hljs-number\">100<\/span>, <span class=\"hljs-attr\">font_weight<\/span>: <span class=\"hljs-string\">\"bold\"<\/span>, <span class=\"hljs-attr\">text_align<\/span>: <span class=\"hljs-string\">\"left\"<\/span>, <span class=\"hljs-attr\">text<\/span>: <span class=\"hljs-string\">\"CG\"<\/span>}},\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 {<span class=\"hljs-attr\">flags<\/span>: <span class=\"hljs-string\">\"layer_apply\"<\/span>, <span class=\"hljs-attr\">gravity<\/span>: <span class=\"hljs-string\">\"north_west\"<\/span>, <span class=\"hljs-attr\">x<\/span>: <span class=\"hljs-number\">20<\/span>, <span class=\"hljs-attr\">y<\/span>: <span class=\"hljs-number\">40<\/span>}\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 ],\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">resource_type<\/span>: <span class=\"hljs-string\">'image'<\/span>,\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-attr\">public_id<\/span>: <span class=\"hljs-string\">`<span class=\"hljs-subst\">${publicId}<\/span>`<\/span>,\n\n\u00a0 \u00a0 \u00a0 \u00a0 });\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> uploadResponse = response.secure_url;\n\n\u00a0 \u00a0 \u00a0 \u00a0 res.status(<span class=\"hljs-number\">200<\/span>).json({ uploadResponse });\n\n\u00a0 \u00a0 } <span class=\"hljs-keyword\">catch<\/span> (error: any) {\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-built_in\">console<\/span>.error(error);\n\n\u00a0 \u00a0 \u00a0 \u00a0 res.status(<span class=\"hljs-number\">500<\/span>).json({ <span class=\"hljs-attr\">message<\/span>: error.message });\n\n\u00a0 \u00a0 }\n\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\n\n<p>Let&#8217;s break down what the code snippet above does:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Line 1<\/strong>. Imports the necessary components from Next.js to define the data structure for requests and responses.<\/li>\n\n\n\n<li><strong>Lines 2-7<\/strong>. Imports the Cloudinary Node.js SDK (version 2) and configures it using previously set environment variables, which include the Cloudinary account details.<\/li>\n\n\n\n<li><strong>Line 10<\/strong>. Extracts the image URL from the data sent in the request, corresponding to the DALL-E 3 generated image.<\/li>\n\n\n\n<li><strong>Lines 11-14<\/strong>. This section extracts the image URL and user&#8217;s prompt from the request. The prompt is then used to create a unique identifier for the uploaded image. To ensure uniqueness, the code combines the prompt with random numbers, removes whitespace, and replaces spaces with hyphens for a valid Cloudinary ID.<\/li>\n\n\n\n<li><strong>Lines 15- 24<\/strong><strong>.<\/strong>This block transforms and uploads the image to Cloudinary. The upload also specifies the content as an image resource and assigns a unique public ID on the user\u2019s prompt. The image is transformed using these presets:\n<ul class=\"wp-block-list\">\n<li><code>{border: \"10px_solid_blue\"}<\/code>. Adds a blue 10-pixel border around the image.<\/li>\n\n\n\n<li><code>{radius: 50}<\/code>. Applies a rounded corner effect to the image with a radius of 50 pixels.<\/li>\n\n\n\n<li><code>{color: \"#FFFFFF69\", overlay: {...}}<\/code>. Creates a semi-transparent text overlay on the image, defining its background color, font family, size, weight, alignment, and text, which serves as a watermark.<\/li>\n\n\n\n<li><code>{flags: \"layer_apply\", gravity: \"north_west\", x: 20, y: 40}<\/code>. Aligns the text overlay&#8217;s position by treating it as a separate layer, anchoring it to the top-left corner (&#8220;north_west&#8221;) and then offsetting it by 20 pixels horizontally and 40 pixels vertically.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Lines 25 and 26<\/strong>. Upon successful upload, this part extracts the upload URL from Cloudinary&#8217;s response and sends it back as a successful response in JSON format.<\/li>\n\n\n\n<li><strong>Lines 27-30<\/strong>. Includes an error catch block to handle any potential issues during the upload process.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>You can apply whatever <a href=\"https:\/\/cloudinary.com\/documentation\/image_transformations\" target=\"_blank\" rel=\"noreferrer noopener\">transformations<\/a> you prefer on your generated images. We used the sample above to demonstrate Cloudinary\u2019s capabilities.<\/li>\n<\/ul>\n\n\n\n<p>Next, you&#8217;ll need to send data to the API route. To handle this, create a function named <code>saveImageToCloudinary<\/code> within the <code>src\/app\/pages<\/code> file. Inside this function, you&#8217;ll set the user prompt and generated image URL from OpenAI API as the body of a POST request to your Cloudinary API route. You&#8217;ll also include a button to trigger the <code>saveImageToCloudinary<\/code> function, which only renders if there\u2019s an image to be saved.<\/p>\n\n\n<pre class=\"wp-block-code\" 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> Image <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/image\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { useEffect, useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n\n\u00a0 interface Item {\n\n\u00a0 \u00a0<span class=\"hljs-attr\">id<\/span>: number;\n\n\u00a0 \u00a0text: string;\n\n\u00a0 }\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Home<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n\n\u00a0 \u00a0<span class=\"hljs-keyword\">const<\/span> &#91;url, setUrl] = useState&lt;string&gt;(<span class=\"hljs-string\">\"\"<\/span>);\n\n\u00a0 \u00a0<span class=\"hljs-keyword\">const<\/span> &#91;value, setValue] = useState&lt;string&gt;(<span class=\"hljs-string\">\"\"<\/span>);\n\n\u00a0 \u00a0<span class=\"hljs-keyword\">const<\/span> &#91;loading, setLoading] = useState&lt;boolean | <span class=\"hljs-literal\">undefined<\/span>&gt;(<span class=\"hljs-literal\">false<\/span>);\n\n\u00a0 \u00a0<span class=\"hljs-keyword\">const<\/span> &#91;save, setSave] = useState&lt;boolean | <span class=\"hljs-literal\">undefined<\/span>&gt;(<span class=\"hljs-literal\">false<\/span>);\n\n\u00a0 \u00a0<span class=\"hljs-keyword\">const<\/span> &#91;saveStatus, setSaveStatus] = useState(<span class=\"hljs-literal\">false<\/span>);\n\n\u00a0 \u00a0<span class=\"hljs-keyword\">const<\/span> &#91;items, setItems] = useState&lt;Item&#91;]&gt;(&#91;]);\n\n\u00a0 useEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\n\u00a0 \u00a0<span class=\"hljs-keyword\">const<\/span> savedImages = localStorage.getItem(<span class=\"hljs-string\">\"myImages\"<\/span>);\n\n\u00a0 \u00a0<span class=\"hljs-keyword\">if<\/span> (savedImages) {\n\n\u00a0 \u00a0 \u00a0setItems(<span class=\"hljs-built_in\">JSON<\/span>.parse(savedImages));\n\n\u00a0 \u00a0}\n\n\u00a0 }, &#91;]);\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> handleSave = <span class=\"hljs-function\">(<span class=\"hljs-params\">newItem: Item<\/span>) =&gt;<\/span> {\n\n\u00a0 \u00a0 \u00a0<span class=\"hljs-keyword\">const<\/span> newItems = &#91;...items, newItem];\n\n\u00a0 \u00a0 \u00a0setItems(newItems);\n\n\u00a0 \u00a0 \u00a0localStorage.setItem(<span class=\"hljs-string\">'myImages'<\/span>, <span class=\"hljs-built_in\">JSON<\/span>.stringify(newItems));\n\n\u00a0 \u00a0};\n\n\u00a0 \u00a0<span class=\"hljs-keyword\">const<\/span> saveImageToCloudinary = <span class=\"hljs-keyword\">async<\/span> (e: any) =&gt; {\n\n\u00a0 \u00a0 \u00a0e.preventDefault()\n\n\u00a0 \u00a0 \u00a0setSave(<span class=\"hljs-literal\">true<\/span>)\n\n\u00a0 \u00a0 \u00a0<span class=\"hljs-keyword\">try<\/span> {\n\n\u00a0 \u00a0 \u00a0 \u00a0<span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> fetch(<span class=\"hljs-string\">'\/api\/cloudinary'<\/span>, {\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">'POST'<\/span>,\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span class=\"hljs-attr\">headers<\/span>: { <span class=\"hljs-string\">'Content-Type'<\/span>: <span class=\"hljs-string\">'application\/json'<\/span> },\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span class=\"hljs-attr\">body<\/span>: <span class=\"hljs-built_in\">JSON<\/span>.stringify({ url, value }), <span class=\"hljs-comment\">\/\/ send the string as a JSON object<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0});\n\n\u00a0 \u00a0 \u00a0 \u00a0<span class=\"hljs-comment\">\/\/ Handle success, such as updating UI or showing a success message<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0<span class=\"hljs-keyword\">if<\/span> (response.ok) {\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span class=\"hljs-keyword\">const<\/span> data = <span class=\"hljs-keyword\">await<\/span> response.json();\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0handleSave(data.uploadResponse); <span class=\"hljs-comment\">\/\/ Add to items array<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 setSave(<span class=\"hljs-literal\">false<\/span>);\n\n\u00a0 \u00a0 \u00a0 \u00a0 onSaveImage()\n\n\u00a0 \u00a0 \u00a0 }\n\n\u00a0 \u00a0 } <span class=\"hljs-keyword\">catch<\/span> (error) {\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\">\"Error sending prompt:\"<\/span>, error);\n\n\u00a0 \u00a0 }\n\n\u00a0 }\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> onSaveImage = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\n\u00a0 \u00a0 setSaveStatus(<span class=\"hljs-literal\">true<\/span>);\n\n\u00a0 \u00a0 setTimeout(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> setSaveStatus(<span class=\"hljs-literal\">false<\/span>), <span class=\"hljs-number\">2000<\/span>); <span class=\"hljs-comment\">\/\/ Reset status after 2 seconds<\/span>\n\n\u00a0 };\n\n\u00a0 <span class=\"hljs-keyword\">const<\/span> handlePrompt = <span class=\"hljs-keyword\">async<\/span> (e: any) =&gt; {\n\n\u00a0 ...\n\n\u00a0 }\n\n<span class=\"hljs-keyword\">return<\/span> ( \u00a0 \u00a0\n\n\u00a0 <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex min-h-screen flex-col items-center justify-between p-10\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 ... \/\/App Title\n\n{url &amp;&amp;\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex flex-col items-center justify-center\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Image<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{url}<\/span> <span class=\"hljs-attr\">onLoadingComplete<\/span>=<span class=\"hljs-string\">{()<\/span> =&gt;<\/span> setLoading(false)} width={500} height={500} alt=\"ai image\" \/&gt;\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 {!loading &amp;&amp;\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"submit\"<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{saveImageToCloudinary}<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex justify-center text-white border bg-blue-700 hover:bg-blue-800 font-medium relative rounded-lg text-xs px-2.5 py-2.5 text-center mt-3\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 {save ? <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Loader<\/span> \/&gt;<\/span> : \"Save image\"}\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 {saveStatus &amp;&amp; <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"text-center\"<\/span>&gt;<\/span>Image saved!<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>}\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 }\n\n\u00a0 \u00a0 \u00a0 \u00a0 }\n\n\u00a0 \u00a0 \u00a0 ... \/\/ prompt input and button\n\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span><\/span>\n\n\u00a0 )\n\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Upon a successful upload, the upload URL from Cloudinary is stored in the browser storage. The browser storage serves as a database to store multiple image URLs a user saves over time. Then, a state variable named items is created using the <code>useState<\/code> hook to manage an array of image objects. For the browser storage, <code>localStorage<\/code> is used to store the current items array (containing all saved image data) as a JSON string under the <code>myImages<\/code> key.<\/p>\n\n\n\n<p>While this project uses browser storage (<code>localStorage<\/code>) to save the image URL, consider using a proper database like <a href=\"https:\/\/www.mongodb.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">MongoDB<\/a> or <a href=\"https:\/\/firebase.google.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Firebase<\/a> for a more robust and scalable solution, especially if the project contains many users or values.<\/p>\n\n\n\n<p>The items state variable is initialized as an empty array. On component mount, the <code>useEffect<\/code> hook retrieves items (saved images) from the <code>localStorage<\/code> and updates the items state. Then, a function, <code>handleSave<\/code>, is created to update the component state and <code>localStorage<\/code> whenever a new image is added. This way, even if the user refreshes the page, the saved images, including the latest one, will still be there. This approach allows the code to access and utilize previously saved images stored in <code>localStorage<\/code> during component initialization.<\/p>\n\n\n\n<p>This implementation currently focuses on saving the image URL. However, it can be enhanced by storing additional image metadata, such as creation date, tags, or descriptions, which can improve image organization and searchability.<\/p>\n\n\n\n<p>An <code>onSaveImage<\/code> function is also created. This function conditionally displays a message indicating the image has been saved upon a successful response from Cloudinary. To store images, the page should look like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img width=\"1024\" height=\"511\" data-public-id=\"Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-5\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-5.png\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_1024,h_511,c_scale\/f_auto,q_auto\/v1726598969\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-5\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-5.png?_i=AA\" alt=\"\" class=\"wp-post-35703 wp-image-35708\" data-format=\"png\" data-transformations=\"f_auto,q_auto\" data-version=\"1726598969\" data-seo=\"1\" srcset=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598969\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-5\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-5.png?_i=AA 1600w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598969\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-5\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-5.png?_i=AA 300w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598969\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-5\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-5.png?_i=AA 768w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598969\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-5\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-5.png?_i=AA 1024w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726598969\/Web_Assets\/blog\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-5\/blog-Building-an-AI-Image-Manager-With-DALL-E-and-Cloudinary-5.png?_i=AA 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Create an Image Database<\/h2>\n\n\n\n<p>Now, you&#8217;ll need an image manager to show, delete, and copy the image URL of your saved images. To create the image manager, navigate to the <code>src\/pages<\/code> folder, create a new folder called images, and add a new file, <code>page.tsx<\/code>. The file will serve as an image manager to display the saved images. Then, head back to the <code>src\/app\/pages<\/code> file and create a button to navigate to the <code>images\/page.tsx<\/code> file as shown below.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ src\/app\/pages.tsx<\/span>\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Home<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{ \u00a0\n\n\u00a0 ... <span class=\"hljs-comment\">\/\/ hooks and functions<\/span>\n\n<span class=\"hljs-keyword\">return<\/span> ( \u00a0 \u00a0\n\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex min-h-screen flex-col items-center justify-between p-10\"<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex items-center justify-between w-full\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"relative text-xl font-semibold capitalize \"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 AI image manager with Dall-E 3 and Cloudinary\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Link<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex items-center justify-center capitalize text-white bg-blue-700 hover:bg-blue-800 font-medium rounded-lg text-xs px-3.5 py-2.5\"<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"\/images\/page\"<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span>&gt;<\/span>image database<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">FaAngleRight<\/span> <span class=\"hljs-attr\">size<\/span>=<span class=\"hljs-string\">{14}<\/span> \/&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Link<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0 \u00a0 \u00a0 ... \/\/ image and upload button\n\n\u00a0 \u00a0 \u00a0 ... \/\/ prompt input and button\n\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span><\/span>\n\n\u00a0 )\n\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In the <code>images\/page.tsx<\/code> file, you\u2019ll use the <code>useEffect<\/code> hook to automatically load the saved images from the browser storage and display them, with the newest appearing first.\u00a0<\/p>\n\n\n\n<p>To display the image with the delete and copy functionality, you\u2019ll first create a <code>deleteImage<\/code> function to remove a selected image from the displayed list, update the application state, and synchronize the saved images in browser storage.<\/p>\n\n\n\n<p>You&#8217;ll iterate through the images state array, generating two buttons for each image:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Delete <\/strong><strong>b<\/strong><strong>utton<\/strong><strong>.<\/strong> This button lets users delete the image from the browser storage.<\/li>\n\n\n\n<li><strong>Copy URL button. <\/strong>This button, wrapped in a <code>CopyToClipboard<\/code> component, lets users copy the image URL to their clipboard. The <code>CopyToClipboard<\/code> component handles the copy functionality and accepts two props:\n<ul class=\"wp-block-list\">\n<li><code>text={img}<\/code>. Defines the text to be copied, which is the image URL stored in the <code>img<\/code> variable.<\/li>\n\n\n\n<li><code>onCopy={onCopyText}<\/code>. A function that triggers upon successful copying.\u00a0<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p>The function <code>onCopyText<\/code> will display a visual confirmation message once a user copies the URL.<\/p>\n\n\n\n<p>Update the <code>images\/page.tsx<\/code> file as shown below.<\/p>\n\n\n\n<figure class=\"wp-block-embed\"><div class=\"wp-block-embed__wrapper\">\nhttps:\/\/gist.github.com\/ugwutotheeshoes\/1a2ec5b8633126a474e0955846a101a6\n<\/div><\/figure>\n\n\n\n<p>After applying the necessary changes above, the app should look like this:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Test the Application<\/h2>\n\n\n\n<p>To test the app, enter a prompt, DALL-E 3 will generate a unique image based on the input, then click <strong>Save<\/strong> to store it in Cloudinary and place the returned URL in local storage. The images will appear in a gallery where the user can delete them or copy their URLs. The application will <a href=\"https:\/\/www.loom.com\/share\/6450ba6985b64431aabb07365fab8476?sid=36274a32-e7f5-4434-b218-0e7625570cb4\">look like this<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>This blog post provides a step-by-step guide to building an AI image manager using DALL-E 3 and Cloudinary. The project can be enhanced by integrating image editing functionalities, search capabilities based on image metadata, and user authentication for private image storage. <a target=\"_blank\" href=\"https:\/\/cloudinary.com\/\" rel=\"noreferrer noopener\">Sign up for a free Cloudinary account today<\/a> to handle all your image management and transformation needs.<\/p>\n\n\n\n<p>And if you found this post helpful and would like to learn more, feel free to join the <a target=\"_blank\" href=\"https:\/\/community.cloudinary.com\/\" rel=\"noreferrer noopener\"><u>Cloudinary Community forum<\/u><\/a> and its associated <a target=\"_blank\" href=\"https:\/\/discord.com\/invite\/cloudinary\" rel=\"noreferrer noopener\"><u>Discord<\/u><\/a>.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Resources<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/platform.openai.com\/docs\/guides\/images\" target=\"_blank\" rel=\"noreferrer noopener\">DALL-E 3 Documentation<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/cloudinary.com\/documentation\" target=\"_blank\" rel=\"noreferrer noopener\">Cloudinary Documentation<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.npmjs.com\/package\/react-copy-to-clipboard\" target=\"_blank\" rel=\"noreferrer noopener\">react-copy-to-clipboard<\/a><a href=\"https:\/\/www.npmjs.com\/package\/react-copy-to-clipboard\" target=\"_blank\" rel=\"noreferrer noopener\"> Package<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Artificial intelligence (AI) has transformed image creation, enabling us to design landscapes or dreamlike portraits with a few words. This newfound creativity comes with the challenge of managing a constantly growing library of images, as traditional cloud storages lack the features needed for efficient delivery and organization. There\u2019s a need for a viable storage option [&hellip;]<\/p>\n","protected":false},"author":87,"featured_media":35710,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[336,25,409],"class_list":["post-35703","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-ai","tag-asset-management","tag-generative-ai"],"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>Building an AI Image Manager With DALL-E and Cloudinary<\/title>\n<meta name=\"description\" content=\"Artificial intelligence (AI) has transformed image creation, enabling us to design landscapes or dreamlike portraits with a few words. This newfound\" \/>\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\/building-ai-image-manager-dall-e-cloudinary\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Building an AI Image Manager With DALL-E and Cloudinary\" \/>\n<meta property=\"og:description\" content=\"Artificial intelligence (AI) has transformed image creation, enabling us to design landscapes or dreamlike portraits with a few words. This newfound\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2024-09-17T14:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-09-17T18:52:04+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1726266233\/dalle3_cloudinary_media_storage-blog\/dalle3_cloudinary_media_storage-blog-jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"1100\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"melindapham\" \/>\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\/building-ai-image-manager-dall-e-cloudinary#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary\"},\"author\":{\"name\":\"melindapham\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\"},\"headline\":\"Building an AI Image Manager With DALL-E and Cloudinary\",\"datePublished\":\"2024-09-17T14:00:00+00:00\",\"dateModified\":\"2024-09-17T18:52:04+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary\"},\"wordCount\":2026,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726266233\/dalle3_cloudinary_media_storage-blog\/dalle3_cloudinary_media_storage-blog.jpg?_i=AA\",\"keywords\":[\"AI\",\"Asset Management\",\"Generative AI\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2024\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary\",\"url\":\"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary\",\"name\":\"Building an AI Image Manager With DALL-E and Cloudinary\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726266233\/dalle3_cloudinary_media_storage-blog\/dalle3_cloudinary_media_storage-blog.jpg?_i=AA\",\"datePublished\":\"2024-09-17T14:00:00+00:00\",\"dateModified\":\"2024-09-17T18:52:04+00:00\",\"description\":\"Artificial intelligence (AI) has transformed image creation, enabling us to design landscapes or dreamlike portraits with a few words. This newfound\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726266233\/dalle3_cloudinary_media_storage-blog\/dalle3_cloudinary_media_storage-blog.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726266233\/dalle3_cloudinary_media_storage-blog\/dalle3_cloudinary_media_storage-blog.jpg?_i=AA\",\"width\":2000,\"height\":1100},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Building an AI Image Manager With DALL-E and 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\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\",\"name\":\"melindapham\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"caption\":\"melindapham\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Building an AI Image Manager With DALL-E and Cloudinary","description":"Artificial intelligence (AI) has transformed image creation, enabling us to design landscapes or dreamlike portraits with a few words. This newfound","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\/building-ai-image-manager-dall-e-cloudinary","og_locale":"en_US","og_type":"article","og_title":"Building an AI Image Manager With DALL-E and Cloudinary","og_description":"Artificial intelligence (AI) has transformed image creation, enabling us to design landscapes or dreamlike portraits with a few words. This newfound","og_url":"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary","og_site_name":"Cloudinary Blog","article_published_time":"2024-09-17T14:00:00+00:00","article_modified_time":"2024-09-17T18:52:04+00:00","og_image":[{"width":2000,"height":1100,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1726266233\/dalle3_cloudinary_media_storage-blog\/dalle3_cloudinary_media_storage-blog-jpg?_i=AA","type":"image\/jpeg"}],"author":"melindapham","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary"},"author":{"name":"melindapham","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9"},"headline":"Building an AI Image Manager With DALL-E and Cloudinary","datePublished":"2024-09-17T14:00:00+00:00","dateModified":"2024-09-17T18:52:04+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary"},"wordCount":2026,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726266233\/dalle3_cloudinary_media_storage-blog\/dalle3_cloudinary_media_storage-blog.jpg?_i=AA","keywords":["AI","Asset Management","Generative AI"],"inLanguage":"en-US","copyrightYear":"2024","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary","url":"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary","name":"Building an AI Image Manager With DALL-E and Cloudinary","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726266233\/dalle3_cloudinary_media_storage-blog\/dalle3_cloudinary_media_storage-blog.jpg?_i=AA","datePublished":"2024-09-17T14:00:00+00:00","dateModified":"2024-09-17T18:52:04+00:00","description":"Artificial intelligence (AI) has transformed image creation, enabling us to design landscapes or dreamlike portraits with a few words. This newfound","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726266233\/dalle3_cloudinary_media_storage-blog\/dalle3_cloudinary_media_storage-blog.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726266233\/dalle3_cloudinary_media_storage-blog\/dalle3_cloudinary_media_storage-blog.jpg?_i=AA","width":2000,"height":1100},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/building-ai-image-manager-dall-e-cloudinary#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Building an AI Image Manager With DALL-E and 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":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9","name":"melindapham","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","caption":"melindapham"}}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1726266233\/dalle3_cloudinary_media_storage-blog\/dalle3_cloudinary_media_storage-blog.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/35703","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\/87"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=35703"}],"version-history":[{"count":1,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/35703\/revisions"}],"predecessor-version":[{"id":35709,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/35703\/revisions\/35709"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/35710"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=35703"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=35703"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=35703"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}