<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Cloudinary</title>
    <description>The latest articles on DEV Community by Cloudinary (@cloudinary).</description>
    <link>https://dev.to/cloudinary</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F7286%2F8677e80f-fe6d-40e5-a9d6-c72af0042cd8.png</url>
      <title>DEV Community: Cloudinary</title>
      <link>https://dev.to/cloudinary</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cloudinary"/>
    <language>en</language>
    <item>
      <title>Say It With Flowers: BouqAIs with the Tussie-Mussie Generator</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Sat, 09 May 2026 17:19:01 +0000</pubDate>
      <link>https://dev.to/cloudinary/say-it-with-flowers-bouqais-with-the-tussie-mussie-generator-3do0</link>
      <guid>https://dev.to/cloudinary/say-it-with-flowers-bouqais-with-the-tussie-mussie-generator-3do0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The &lt;strong&gt;&lt;a href="https://tussie-mussies.netlify.app" rel="noopener noreferrer"&gt;Tussie-Mussie Generator&lt;/a&gt;&lt;/strong&gt; is a web app for anyone who wants to send a meaningful floral e-card with an AI-generated "tussie mussie", a type of small nosegay. It uses the Victorian language of flowers to compose a bouquet, generates an image with Gemini, and sends it via Resend. This post covers &lt;br&gt;
a Mother's Day relaunch: fixing a broken email integration and swapping the AI image model.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What changed in this relaunch
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Migrated email sending from Mailgun to Resend, including a Cloudflare serverless function&lt;/li&gt;
&lt;li&gt;Fixed DNS configuration across Porkbun, Netlify, and Resend for a custom subdomain&lt;/li&gt;
&lt;li&gt;Replaced the previous image generator with Gemini for accurate tussie mussie generation&lt;/li&gt;
&lt;li&gt;Refactored the UI with Astro + Vue and impeccable.style for AI-assisted critique&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Have you ever wanted to build an e-card system that would allow you to send little ai-generated bouquets to your friends? No? Yes? Read on, gentle reader!&lt;/p&gt;

&lt;p&gt;Last year, I onboarded to my new job by building this sample app that lets you use the Victorian language of flowers to compose a bouquet. An image-heavy app like this is a great candidate for hosting and optimizing via Cloudinary, so all the flower images are stored there. An image of the composed bouquet is generated by Gemini, and then sent via Resend to the recipient of your choice. The app looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj3w0zh47qr8wxul07kfk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj3w0zh47qr8wxul07kfk.png" alt="Tussie Mussie Generator" width="800" height="722"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and the generated e-card looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9hl2hu7bcummpftq8k7x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9hl2hu7bcummpftq8k7x.png" alt="demo" width="800" height="1245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I launched this app a year ago, and it's had an interesting lifespan. I wanted to relaunch it for Mother's Day and realized there was some refactoring to do! &lt;/p&gt;

&lt;p&gt;Here are a few of the gotchas I dealt with:&lt;/p&gt;

&lt;p&gt;Keeping the core: The basis of this app is its dataset, which I created a while ago on &lt;a href="https://www.kaggle.com/datasets/jenlooper/language-of-flowers" rel="noopener noreferrer"&gt;Kaggle&lt;/a&gt; as a useful version of the Victorian "Language of Flowers", scraped from the &lt;a href="https://www.almanac.com/flower-meanings-language-flowers#flower-meanings" rel="noopener noreferrer"&gt;Farmer's Almanac&lt;/a&gt;. Here's an example of what that looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz5l8fjmwlta0yy1trhoa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz5l8fjmwlta0yy1trhoa.jpg" alt="Almanac version of florilege" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your idea is centered around a solid dataset, you can't go wrong. Once I had the list, it took me a while to scrape images of the flowers in question and store them, optimized, on Cloudinary. But that work was done a year ago. So two things remained for the relaunch: refactoring the UI and fixing the bits that stopped working, namely the e-card generator.&lt;/p&gt;

&lt;h2&gt;
  
  
  Refactoring the UI
&lt;/h2&gt;

&lt;p&gt;This app is built using my preferred stack, namely Astro + Vue. After a year, it was definitely time to upgrade, including fixing the tailwind integration which relied on an outdated package. Cursor made short work of that - it's one of the best uses of AI software agents.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: periodically ask your AI to plan the removal of any redundant files or code while you complete refactors. There are usually vestiges that can be deleted.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I used a wildly useful AI skill to refactor my admittedly basic UI: &lt;a href="https://impeccable.style/" rel="noopener noreferrer"&gt;impeccable.style&lt;/a&gt;. Install these skills on your machine and as you work with any AI generated UI suggestions, run &lt;code&gt;/impeccable critique&lt;/code&gt; to reality-check your work for taste and style. I'm now using impeccable on all my UIs - it also helped me tidy &lt;a href="https://jenlooper.com" rel="noopener noreferrer"&gt;my portfolio&lt;/a&gt;. The UI went from this: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6luq0346nkg7qkrvbv2.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6luq0346nkg7qkrvbv2.webp" alt="old UI" width="800" height="638"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;...to the UI shown above. I like it a lot. &lt;/p&gt;




&lt;h2&gt;
  
  
  Fixing the E-Card functionality
&lt;/h2&gt;

&lt;p&gt;This app caused me a horrible panic after launch last year, ruining an anniversary dinner, due to a kerfuffle with Mailgun, the original e-card sending mechanism. I was shocked by an enormous overage charge due to hacking of their systems, and I shut the entire thing down as fast as I could. To relaunch, I needed to find a new service to send email via API. Once again, Cursor to the rescue. &lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://resend.com/" rel="noopener noreferrer"&gt;Resend&lt;/a&gt;, a new email service that is much more solid than my previous choice. Let us hope that we have smooth sailing from now on! The integration includes a serverless function that you can host on Cloudflare, and works well as soon as you verify a domain. That last part took me the longest time - the gotcha is that since I bought the domain on Porkbun, I had to edit DNS records there to serve emails from a subdomain. The site is hosted on Netlify, so I had to build the subdomain there, and then the DNS verified on Resend. It's the kind of task that you don't do often, so it's easy to miss a step. But all is set now, and the emails are sending!&lt;/p&gt;

&lt;p&gt;I also had to reconsider how to build the AI generated bouquets. I'm out of credits on the service I previously chose to generate the bouquets, and they were never quite right anyway (generated as standard bouquets rather than true tussie mussies which have a silver filigree holder). It's crazy to see how AI has evolved for this esoteric imagery over the past year - now Gemini can accurately compose a tussie mussie in its holder, given a specific prompt.&lt;/p&gt;

&lt;p&gt;Enjoy these little bouquets! &lt;a href="https://tussie-mussies.netlify.app/" rel="noopener noreferrer"&gt;Send one to your Mom today&lt;/a&gt;!&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://link.cloudinary.com/uoTiK" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>astro</category>
      <category>vue</category>
    </item>
    <item>
      <title>How to Win a Hackathon</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Sun, 03 May 2026 03:43:08 +0000</pubDate>
      <link>https://dev.to/cloudinary/how-to-win-a-hackathon-1377</link>
      <guid>https://dev.to/cloudinary/how-to-win-a-hackathon-1377</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A sponsor’s perspective&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This blogpost is written on the heels of a few amazing weekends spent knee-deep in students in Toronto and Los Angeles at Hack Canada and LA Hacks, engaging almost 2000 students in person in total. Managing a sponsor track, I have some thoughts on good ways to present yourselves when angling your project towards a sponsor prize.&lt;/p&gt;

&lt;p&gt;For those not as familiar with how student hackathons work, they are usually comprised of an organized weekend populated by college undergrads (or older, or younger! high schoolers are increasingly getting into the mix). Over about 36 hours, students form teams and try to build software that meets a need, solves a problem, or proves a point. Some hackathons focus on themes around social good, like Hack Canada which challenged students to build something of special interest to Canada. Others have several themes and a full roster of sponsored options. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Hackathons are a bastion of the cozy web! It’s a delight to see students embracing the freedom to create weird, useless, and wildly creative apps like AeroMaxx, a way to gauge influencers’ aerodynamic-maxxing (Taylor Swift is more aerodynamic than Clavicular, did you know?). LA Hacks even had a track embracing the “most questionable” hack, won by “Yes? Or Yes!”, a hack that had literal goldfish helping make decisions and trigger AI agents to do things like break up with a girl friend, quit a job, and post to social media. Great projects. Please keep building this kind of weird stuff.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgn9ghxj1wt5pctpuhve0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgn9ghxj1wt5pctpuhve0.png" alt="MagByte" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;a cozy interface for roommates to manage their shopping lists: &lt;a href="https://devpost.com/software/magbite?_gl=1*1nkfo64*_gcl_au*MjA0ODI2ODI1OC4xNzcyOTAyNTc4*_ga*Mzk1NTQ4NjAwLjE3NzI5MDI1Nzk.*_ga_0YHJK3Y10M*czE3Nzc3Nzc1MDMkbzM4JGcxJHQxNzc3Nzc3NjM5JGo0OCRsMCRoMA.." rel="noopener noreferrer"&gt;MagByte&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s a great moment to let your creativity run wild:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi55ce5tfipfpjajllj59.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi55ce5tfipfpjajllj59.png" alt="goldfish q&amp;amp;a" width="800" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://devpost.com/software/yes-or-yes?_gl=1*nv0asj*_gcl_au*MjA0ODI2ODI1OC4xNzcyOTAyNTc4*_ga*Mzk1NTQ4NjAwLjE3NzI5MDI1Nzk.*_ga_0YHJK3Y10M*czE3Nzc3Nzc1MDMkbzM4JGcxJHQxNzc3Nzc4MTA2JGo1NiRsMCRoMA.." rel="noopener noreferrer"&gt;Yes? Or Yes!&lt;/a&gt; LA Hacks most unhinged hack winner - goldfish help you make decisions and then trigger agentic workflows. Good luck!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’ve been working the hackathon circuit as a developer advocate for at least ten years, including the mess during COVID, and the major difference nowadays between the earlier events is, unsurprisingly, the heavy use of AI tooling that both speeds up delivery, polishes the final product, and gets more software shipped, way faster. Instead of watching demo videos, judges can actively interact with an almost production-ready app, shockingly quickly.&lt;/p&gt;

&lt;p&gt;Still, there are some tips and tricks that I’d like to share based on a few recent observations of these very fun events. Here are five things to keep in mind as you go for a win!&lt;/p&gt;

&lt;h2&gt;
  
  
  Who’s it for?
&lt;/h2&gt;

&lt;p&gt;A lot of folks are attracted to social-good type projects, and that’s very laudable. I love them, personally, and tend to mark them highly in my judging rubric. I just want to caution hackathon participants against going after this vector with a “tech savior complex”. It’s easy to think that tech can solve all the things. And tech has made great strides on devices such as most smart phones to help people with various accessibility issues such as those who are blind, color blind, experience hearing loss, or are mobility-challenged. &lt;/p&gt;

&lt;p&gt;But if you are going to create a project for a given sector of users, it’s critical to come at these projects with an open mindset and humility, and to work with people who have these actual challenges. Working on an app for older people? Call your Grandma or Grandpa before you start building. It’s likely they can guide you to refining your app to make it much more useful.&lt;/p&gt;

&lt;p&gt;At Hack Canada, teams succeeded who spent time talking to local security guards as they built, to ensure that their safety-oriented hack would be useful. It’s all about finding your customer and/or user, and building backwards from that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Power to the People
&lt;/h2&gt;

&lt;p&gt;Sponsors and mentors are often available to help you all the way through the event, so make use of them! Take some time before you start working to talk to sponsors and see if they can help you start to ideate. Learn about the various sponsors and what they are looking for. Some are looking for product feedback, some are hiring, others may have another agenda. &lt;/p&gt;

&lt;p&gt;Maybe you’ll find a product that you never heard of and will make a nice professional connection. Get your LinkedIn app ready to scan codes! This is a great moment to network.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F82424uizvh1gzcbpfr3z.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F82424uizvh1gzcbpfr3z.jpg" alt="Jen and Raya" width="582" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At your service at the booth!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Skill Up
&lt;/h2&gt;

&lt;p&gt;The use of AI tools has accelerated the build phase of a hackathon almost beyond recognition. When I was judging hackathons around 2016 or so, the projects produced were super shaky - often the hacker created a video at the moment that the app worked, and showed that to a judge as it was highly unlikely they could ever get it working again! This has all changed when AI has become such an important build partner.&lt;/p&gt;

&lt;p&gt;The use of AI, however, shifts the focus of a hackathon away from the software engineering tasks that now can be offloaded to agents (with supervision, of course!). Now, you need to spend a good amount of time getting set up to make your AI as successful and efficient as you can. Many vendors have created tools to help; we at Cloudinary produced a Skills Pack for this purpose: &lt;a href="https://github.com/cloudinary-devs/skills" rel="noopener noreferrer"&gt;https://github.com/cloudinary-devs/skills&lt;/a&gt;. Using tools such as these will effectively give official context to your app, preventing hallucinations and other wasting of time. &lt;/p&gt;

&lt;h2&gt;
  
  
  Sponsormaxxing and platformmaxxing
&lt;/h2&gt;

&lt;p&gt;There’s a trend towards “sponsormaxxing” - LA Hacks even had a track for this. The term refers to the incorporation of as many sponsors’ platforms as possible in a given hack. I think it’s a fun challenge, but there’s a risk: if you want to win a sponsor track, you’ll likely be judged on deep integration with their platform. &lt;/p&gt;

&lt;p&gt;In our case at Cloudinary, I’m looking for not just the use of the platform as a place to dump images and video, but a deep use of the APIs, for example optimizing and transforming images in an AI pipeline with the goal of constructing videos (as you can do with our winning hack from LA Hacks, StudyO!). Going for a sponsor track prize? Focus on deep integrations.&lt;/p&gt;

&lt;p&gt;Going deep, however, can also be risky! There’s the risk of “platformmaxxing” - using every bell and whistle of a platform to try it out. You may need to be a bit strategic here, as you might max out your credits by piling on as many platform aspects as you can. Watch out for signs of maxing out your credits and plan accordingly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Folgg9609kdf96demfwx8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Folgg9609kdf96demfwx8.png" alt="AeroMaxx" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Speaking of maxxing: &lt;a href="https://devpost.com/software/aeromaxx?_gl=1*vb7ib8*_gcl_au*MjA0ODI2ODI1OC4xNzcyOTAyNTc4*_ga*Mzk1NTQ4NjAwLjE3NzI5MDI1Nzk.*_ga_0YHJK3Y10M*czE3Nzc3Nzc1MDMkbzM4JGcxJHQxNzc3Nzc3OTE0JGoxNiRsMCRoMA.." rel="noopener noreferrer"&gt;AeroMaxx&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Perfect the Pitch
&lt;/h2&gt;

&lt;p&gt;Judges have only a few minutes per team so you need to make your best impression right away. Come ready to demo with your storytelling skills well in hand. Do some trial runs prior, and get feedback on your presentation skills. Lead with the use case, show the demo, and field questions. A good strategy for a 2-3 minute hackathon pitch is to focus on the problem you see, the solution you propose, and the impact you predict. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hook (15 seconds): Introduce yourself, state the problem you are solving, and share a relatable scenario or story.&lt;/li&gt;
&lt;li&gt;Demo (60-90 seconds): Walk the audience through your core feature&lt;/li&gt;
&lt;li&gt;Tech (30 seconds): Briefly explain the most impressive technical hurdle you overcame or the tech stack that powers the hack.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: Consider recording a video of your demo just in case there’s a horrible glitch, and practice getting over a glitchy demo. You can do it!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvfusg6w07felvou9vje9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvfusg6w07felvou9vje9.jpg" alt="pitch me" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pitching can be intense!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Winning a hackathon is a great goal, but don’t forget that being accepted into some of these selective hackathons, participating, and building a piece of software that you can be proud of is very laudable as well. Wishing you the best of luck! And maybe we will see you there.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://link.cloudinary.com/uoBac" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>career</category>
      <category>community</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>6 Free Cloudinary Courses That Teach You to Build Media-Optimized Apps</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Thu, 30 Apr 2026 18:33:51 +0000</pubDate>
      <link>https://dev.to/cloudinary/6-free-cloudinary-courses-that-teach-you-to-build-media-optimized-apps-51m2</link>
      <guid>https://dev.to/cloudinary/6-free-cloudinary-courses-that-teach-you-to-build-media-optimized-apps-51m2</guid>
      <description>&lt;p&gt;The Cloud to Crowd (C2C) curriculum is Cloudinary's free 6-course learning suite for developers who want to build and ship applications with production-quality image and video handling. It covers everything from Cloudinary basics to AI-powered transformations paired with hands-on projects at every step and a free certificate when you complete each course.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fun fact: community member Jerome Hardaway of Vets Who Code, one of our nonprofit partners in the Creators Community program, coined the "Cloud to Crowd" term!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What You'll Be Able to Build After This Curriculum
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Upload, manage, and transform media assets using the Cloudinary API&lt;/li&gt;
&lt;li&gt;Optimize images and video for performance in a Next.js app&lt;/li&gt;
&lt;li&gt;Apply AI-powered features: background removal, generative fill, auto-tagging&lt;/li&gt;
&lt;li&gt;Use the Python SDK to automate media workflows on the backend&lt;/li&gt;
&lt;li&gt;Deliver video efficiently with format and quality auto-selection&lt;/li&gt;
&lt;li&gt;Ship a real portfolio project that demonstrates all of the above&lt;/li&gt;
&lt;li&gt;Earn a Cloudinary certificate and qualify for the Creators Community&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevy6pbfnmkb347ezgdgs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevy6pbfnmkb347ezgdgs.png" alt="Cloudinary Academy Courseware" width="800" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why We Built This
&lt;/h2&gt;

&lt;p&gt;Getting images and video right in a production app is harder than most tutorials suggest. Resize, format, optimize, deliver — each step has tradeoffs, and most learning resources skip the messy middle. The C2C courses are designed to fill that gap: structured, project-driven, and free.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 6 Free Courses
&lt;/h2&gt;

&lt;p&gt;All courses are self-paced and free at &lt;a href="https://training.cloudinary.com/pages/c2c" rel="noopener noreferrer"&gt;https://training.cloudinary.com/pages/c2c&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Course 1: Media IQ for Developers
&lt;/h3&gt;

&lt;p&gt;Enroll: &lt;a href="https://training.cloudinary.com/learn/course/devrel-c2c-intro" rel="noopener noreferrer"&gt;https://training.cloudinary.com/learn/course/devrel-c2c-intro&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The starting point for the first three courses in this suite. This course covers the core Cloudinary concepts: cloud name, API keys, the Media Library, and your first upload and transformation. No prior Cloudinary experience needed. By the end, you'll have a free account set up and understand how assets move through the Cloudinary pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You'll learn to:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose the right image formats for your use case, understanding when to use JPEG, PNG, WebP, AVIF, or SVG based on content type, browser support, and performance trade-offs&lt;/li&gt;
&lt;li&gt;Apply strategic optimization techniques including responsive images with automatic format/quality selection and intelligent cropping with gravity detection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fry0mgv63008yznpxadxa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fry0mgv63008yznpxadxa.png" alt="Heavy Watch image" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Course 2: Media IQ for Developers with Next.js
&lt;/h3&gt;

&lt;p&gt;Enroll: &lt;a href="https://training.cloudinary.com/learn/course/devrel-c2c-next" rel="noopener noreferrer"&gt;https://training.cloudinary.com/learn/course/devrel-c2c-next&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The flagship course of the C2C curriculum. You'll build a storefront application (the "CapZone" e-commerce store) using Next.js and the Cloudinary Next.js SDK, applying real-world image optimization patterns throughout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You'll learn to:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build an image-optimized web application using Cloudinary to upload, store, transform, and deliver images on-the-fly through URL-based transformations and CDN delivery&lt;/li&gt;
&lt;li&gt;Apply strategic optimization techniques including responsive images with automatic format/quality selection and intelligent cropping with gravity detection&lt;/li&gt;
&lt;li&gt;Create Next.js components that handle image uploading, display optimized images with overlays and watermarks, and serve different sizes for different contexts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhpe6j2tmei2fq4ao40v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhpe6j2tmei2fq4ao40v.png" alt="CapZone store" width="800" height="676"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The kind of component you'll be writing by the end of this course:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CldImage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next-cloudinary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductImage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CldImage&lt;/span&gt;
      &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;publicId&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;crop&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"fill"&lt;/span&gt;
      &lt;span class="na"&gt;gravity&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"auto"&lt;/span&gt;
      &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"auto"&lt;/span&gt;
      &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"auto"&lt;/span&gt;
      &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Course 3: Media IQ for Developers with AI
&lt;/h3&gt;

&lt;p&gt;Enroll: &lt;a href="https://training.cloudinary.com/learn/course/devrel-c2c-ai" rel="noopener noreferrer"&gt;https://training.cloudinary.com/learn/course/devrel-c2c-ai&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cloudinary's AI-powered transformation features are some of its most practical and useful. This course covers the tools that save the most time in real projects: background removal, generative fill, object-aware cropping, and auto-tagging. This course follows the Next.js course, building AI into the CapZone store by adding a color picker to colorize the hats.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You'll learn to:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remove or replace backgrounds using e_background_removal&lt;/li&gt;
&lt;li&gt;Use generative fill to extend or recompose images&lt;/li&gt;
&lt;li&gt;Apply smart cropping with gravity: "auto" for face- and object-aware results&lt;/li&gt;
&lt;li&gt;Auto-tag assets using AI analysis to make large libraries searchable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4ryvwuokux6f54ai35k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb4ryvwuokux6f54ai35k.png" alt="AI CapZone" width="800" height="811"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Course 4: Media IQ for Developers with Video
&lt;/h3&gt;

&lt;p&gt;Enroll: &lt;a href="https://training.cloudinary.com/learn/course/devrel-c2c-video" rel="noopener noreferrer"&gt;https://training.cloudinary.com/learn/course/devrel-c2c-video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Video is a different beast from images; you manage larger files, adaptive bitrates, more delivery complexity. This course covers the Cloudinary video pipeline from upload to playback. It's the third and final part of the progressive enhancement of the CapZone e-commerce web app that you started prior. In this course you'll add a video to one of the products.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You'll learn to:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upload and transcode video using the Cloudinary API&lt;/li&gt;
&lt;li&gt;Apply video transformations: trim, resize, overlay, add subtitles&lt;/li&gt;
&lt;li&gt;Build playlists and seekbars with subtitles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2xo1znkfbl48ywp9zu6d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2xo1znkfbl48ywp9zu6d.png" alt="Video with playlist on CapZone" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Course 5: Media IQ for Developers with Python
&lt;/h2&gt;

&lt;p&gt;For less JavaScript-focused developers, this course covers Cloudinary's Python SDK: uploading, managing, and transforming assets from any Python environment.&lt;br&gt;
Enroll: &lt;a href="https://training.cloudinary.com/learn/course/devrel-c2c-python" rel="noopener noreferrer"&gt;https://training.cloudinary.com/learn/course/devrel-c2c-python&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You'll learn to:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authenticate and configure the Python SDK&lt;/li&gt;
&lt;li&gt;Upload and tag assets for organization&lt;/li&gt;
&lt;li&gt;Build a small pet store app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq1f7brwa80h9bjk2p7yy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq1f7brwa80h9bjk2p7yy.png" alt="pet store app" width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Course 6: Building a Developer Portfolio with Cloudinary and Next.js
&lt;/h2&gt;

&lt;p&gt;Enroll: &lt;a href="https://training.cloudinary.com/learn/course/devrel-c2c-portfolio" rel="noopener noreferrer"&gt;https://training.cloudinary.com/learn/course/devrel-c2c-portfolio&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The capstone course. You'll apply everything from the earlier modules: upload, transformation, optimization, and AI features — to build a complete portfolio site using React and TypeScript that demonstrates your Cloudinary skills to employers. Focus on your own skills and show them off to the world in a media-rich website.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You'll learn to:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Architect a media-rich portfolio app from scratch&lt;/li&gt;
&lt;li&gt;Combine multiple Cloudinary transformations into a polished UI&lt;/li&gt;
&lt;li&gt;Optimize for performance: lazy loading, responsive breakpoints, format auto-selection&lt;/li&gt;
&lt;li&gt;Deploy a production-ready app you can link from your resume&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4891ntoh6m30xoqdmsvh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4891ntoh6m30xoqdmsvh.png" alt="portfolio" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Who's this for?
&lt;/h2&gt;

&lt;p&gt;The C2C curriculum works for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Early-career developers who want structured, project-based learning beyond "hello world" tutorials&lt;/li&gt;
&lt;li&gt;Career changers building a portfolio they can point to during job interviews&lt;/li&gt;
&lt;li&gt;Frontend developers who've used Cloudinary but want to understand the optimization and AI layers&lt;/li&gt;
&lt;li&gt;Backend developers looking to add media handling to their Python projects&lt;/li&gt;
&lt;li&gt;Bootcamp graduates who want to go from tutorial-follower to someone who can build production media pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Enroll for Free
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to training.cloudinary.com/pages/c2c&lt;/li&gt;
&lt;li&gt;Create a free Cloudinary Academy account — you just need an email address&lt;/li&gt;
&lt;li&gt;Start with Course 1 or jump to the course that matches your current skill level&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All 6 courses are free. Each comes with a completion certificate. Finishing any course and passing its final assessment at 80%+ earns you a credential that qualifies you to apply to the Cloudinary Creators Community, a network with mentorship, Discord access, mini-hack events, and connections to developers worldwide.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Cloudinary Creators Community
&lt;/h2&gt;

&lt;p&gt;The Cloudinary Creators Community (CCC) is a selective, cohort-based program for developers who've completed the C2C curriculum. &lt;/p&gt;

&lt;p&gt;It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access to private Discord channels with Cloudinary's Developer Relations team&lt;/li&gt;
&lt;li&gt;Monthly mini-hack challenges to apply and extend your skills&lt;/li&gt;
&lt;li&gt;Peer review and mentorship from a global network of developers&lt;/li&gt;
&lt;li&gt;Badges, certificates, and holopins with social media shout-outs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can join the Discord server now and participate in community activities even while you're working through the curriculum.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ready to start? → &lt;a href="//training.cloudinary.com/pages/c2c"&gt;training.cloudinary.com/pages/c2c&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This blogpost was crafted with the help of Claude. Banner image by Nano Banana.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://link.cloudinary.com/un3am" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Ship Your Next Great Web App in Record Time with create-cloudinary-react</title>
      <dc:creator>Eric Portis</dc:creator>
      <pubDate>Tue, 07 Apr 2026 19:53:07 +0000</pubDate>
      <link>https://dev.to/cloudinary/ship-your-next-great-web-app-in-record-time-with-create-cloudinary-react-2coa</link>
      <guid>https://dev.to/cloudinary/ship-your-next-great-web-app-in-record-time-with-create-cloudinary-react-2coa</guid>
      <description>&lt;p&gt;Over the past year or two, we've been having a lot of conversations about the future of Cloudinary's &lt;a href="https://www.cloudinary.dev" rel="noopener noreferrer"&gt;Libraries&lt;/a&gt; and &lt;a href="https://cloudinary.com/documentation/cloudinary_sdks" rel="noopener noreferrer"&gt;SDKs&lt;/a&gt;. The conversations have been wide-ranging: the future of front-end frameworks, coding, and AI. It's all been a bit dizzying, and hard to wrap our heads around.&lt;/p&gt;

&lt;p&gt;Late last year, my colleague Raya Straus brought us back down to earth with some good old-fashioned user research. By engaging users in conversations about their current day-to-day experiences, Raya got us to stop worrying about the future, and start thinking about ways we could make developers more successful with Cloudinary &lt;em&gt;right now.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We released the first fruit of Raya's research last month: &lt;a href="https://github.com/cloudinary-devs/create-cloudinary-react" rel="noopener noreferrer"&gt;Cloudinary's React Starter Kit&lt;/a&gt;. If you're building a green-field, media-focused React app, and you're using LLM-powered development tools, we think &lt;code&gt;npx create-cloudinary-react&lt;/code&gt; is the best way to get started.&lt;/p&gt;

&lt;p&gt;Before we get into what it is, let's talk about how and why we built it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Learned From React SDK User Research
&lt;/h2&gt;

&lt;p&gt;We limited our research scope to two of our most popular SDKs: &lt;a href="https://cloudinary.com/documentation/react_integration" rel="noopener noreferrer"&gt;React&lt;/a&gt; and &lt;a href="https://next.cloudinary.dev" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;. We looked at every piece of user feedback for those two SDKs that we could find: support tickets, GitHub issues, chat transcripts, survey responses, and even a number of individual conversations held over email and Zoom, after we reached out to top SDK users.&lt;/p&gt;

&lt;p&gt;Once we'd collected and digested it all, three things stuck out:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Many developers struggle to &lt;em&gt;get started&lt;/em&gt;. The least fun part of any development project is setting up a working development environment; a handful of common config problems are tripping up many developers before they can take their first steps with Cloudinary.&lt;/li&gt;
&lt;li&gt;Once set up, features that solve common use cases were straightforward and people were quickly successful. Cloudinary and our SDKs do a great job of solving common use cases with a minimum of fuss. Great!&lt;/li&gt;
&lt;li&gt;But once use cases get more advanced, developers start to struggle again. Cloudinary is a mature product, and &lt;em&gt;can&lt;/em&gt; do &lt;em&gt;all sorts of things&lt;/em&gt;, but advanced features are more complicated to use. Niche functionality often has fiddlier syntax, less clear error messages, and trickier-to-find documentation. So even though the features are there, developers run into walls when trying to use them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftyr2nrznnt15tjvhphsy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftyr2nrznnt15tjvhphsy.png" alt="A line chart. The x axis is labelled, " width="800" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How the React Starter Kit Works
&lt;/h2&gt;

&lt;p&gt;So, the question we ended up with, was: How can we help developers who are just getting started with Cloudinary, &lt;em&gt;and&lt;/em&gt; folks with complex use cases?&lt;/p&gt;

&lt;p&gt;We think we can address both with one new tool: &lt;code&gt;npx create-cloudinary-react&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Type that into a terminal near you and you'll be led through a wizard, which will ask you a few questions about your environment. Then, &lt;em&gt;it&lt;/em&gt; will spin up a working React project for you, using Vite as a build tool, and handle all of the configuration that people who are just starting to use Cloudinary with React so often get stuck on. The resulting project starts with a simple, one-page website that accepts and displays user-generated image uploads.&lt;/p&gt;

&lt;p&gt;In addition to doing basic configuration, the wizard &lt;em&gt;also&lt;/em&gt; installs &lt;a href="https://cloudinary.com/documentation/cloudinary_llm_mcp" rel="noopener noreferrer"&gt;Cloudinary's LLM-specific tools&lt;/a&gt;, and puts &lt;a href="https://github.com/cloudinary-devs/create-cloudinary-react/blob/main/templates/.cursorrules.template" rel="noopener noreferrer"&gt;a bunch of rules and troubleshooting guidance to help LLMs with Cloudinary's APIs&lt;/a&gt; wherever your particular LLM-powered coding environment looks for context.&lt;/p&gt;

&lt;p&gt;We derived these rules from our user research, by feeding all of the feedback we received about using Cloudinary's React SDK into an LLM and asking it to write rules to address that feedback. From there, we refined, added, and subtracted rules based on our experiences of using and helping other folks use Cloudinary.&lt;/p&gt;

&lt;p&gt;Those rules, in addition to our other LLM-specific tools, significantly improve LLMs' results when prompted with complex tasks.  To show off those capabilities, the initial built project offers a handful of example prompts which you can copy, paste into your agent, and hit the ground running.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;create-cloudinary-react&lt;/code&gt;, we believe we've built something that helps &lt;em&gt;everyone&lt;/em&gt; get started, and &lt;em&gt;also&lt;/em&gt; helps folks who have gone beyond common use cases use all of the nooks and crannies of Cloudinary's extensive feature set. &lt;/p&gt;

&lt;h2&gt;
  
  
  See the React Starter Kit in Action
&lt;/h2&gt;

&lt;p&gt;Here's &lt;code&gt;create-cloudinary-react&lt;/code&gt; in action:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/gmzYabZFUHo"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  How the React Starter Kit performed at Hack Canada 2026
&lt;/h2&gt;

&lt;p&gt;After a few rounds of internal development and testing, we soft-launched the React Starter Kit a few weeks ago -- just before we participated as a sponsor in this year's &lt;a href="https://hackcanada.org/" rel="noopener noreferrer"&gt;Hack Canada&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;A hackathon is a perfect environment for this kind of tool, and the results blew us away. Participants leveraged the Starter Kit to produce some &lt;a href="https://hack-canada-2026.devpost.com/submissions/search?utf8=%E2%9C%93&amp;amp;prize_filter%5Bprizes%5D%5B%5D=97781" rel="noopener noreferrer"&gt;truly incredible projects&lt;/a&gt;, and cited &lt;code&gt;create-cloudinary-react&lt;/code&gt; as part of their success.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/ERRFRz6LQZs"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/pJzrU2F_3r8"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/A5McD0WwqzM"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;That was all the validation we needed to add a &lt;a href="https://cloudinary.com/documentation/react_starter_kit" rel="noopener noreferrer"&gt;React Starter Kit walk through to our documentation&lt;/a&gt; and announce it in this very blog post.&lt;/p&gt;

&lt;p&gt;So, go! &lt;a href="https://github.com/cloudinary-devs/create-cloudinary-react" rel="noopener noreferrer"&gt;Try it out!&lt;/a&gt; And tell us what you think in the comments.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://link.cloudinary.com/un3aq" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>ai</category>
      <category>react</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Upload and Deliver Media in Flask with Cloudinary: Presets, Widget, and Search</title>
      <dc:creator>Sharon Yelenik</dc:creator>
      <pubDate>Mon, 23 Mar 2026 19:07:00 +0000</pubDate>
      <link>https://dev.to/cloudinary/managing-media-files-in-flask-images-videos-and-audio-kdk</link>
      <guid>https://dev.to/cloudinary/managing-media-files-in-flask-images-videos-and-audio-kdk</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Excerpt:&lt;/strong&gt; Cloudinary is a media API for Python/Flask developers that uploads, transforms, and searches images and video without bloating your app or database. It cuts custom upload code and speeds delivery.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This post shows how to handle user uploads and delivery in a Flask app using Cloudinary’s Python SDK, upload presets, the Upload Widget, and search—without storing binaries in your database. The first sections cover setup; then we add client-side and server-side uploads.&lt;/p&gt;

&lt;p&gt;If you need a moderate amount of UGC (portfolios, catalogs), you want uploads off your server, consistent transforms, and searchable metadata.&lt;/p&gt;

&lt;p&gt;You’re building an app for a small business or website, maybe a portfolio site or boutique store, and you know high-quality visuals are key for attracting buyers and showcasing your work. But without a designer on the team, you’re responsible for handling all the images and videos yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you’ll do
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Install and configure Cloudinary in Flask&lt;/li&gt;
&lt;li&gt;Create an upload preset (transforms + auto-tagging)&lt;/li&gt;
&lt;li&gt;Add the Upload Widget and a server-side upload route&lt;/li&gt;
&lt;li&gt;Query assets by tag with the Search API&lt;/li&gt;
&lt;li&gt;Deliver with transformations in templates (e.g. &lt;code&gt;cloudinary_url&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Benefits of Streamlined Media Management
&lt;/h2&gt;

&lt;p&gt;Before we get into the practical steps, here are some of the advantages of establishing a solid media management approach in your Flask application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Less custom code and reduced storage complexity.&lt;/strong&gt; Offloading large media files from your database or filesystem keeps your application simpler and avoids managing bulky binary data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliable uploads that scale.&lt;/strong&gt; A predictable upload flow prevents issues with file size limits, timeouts, or custom route handling, especially when users submit large images or videos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent, high-quality digital media.&lt;/strong&gt; Automated tasks like resizing, compression, tagging, and background removal help keep your images and videos optimized without manual editing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimized, searchable delivery.&lt;/strong&gt; Well-structured metadata and scalable search make assets easier to find, while delivering the right size and format improves performance across devices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By &lt;a href="https://link.cloudinary.com/umWsT" rel="noopener noreferrer"&gt;storing your digital media in Cloudinary&lt;/a&gt; instead of on your server or in your database, you can take advantage of these benefits immediately, while keeping your Flask codebase lightweight and focused on application logic.&lt;/p&gt;

&lt;p&gt;Managing media files in Flask becomes much simpler with the steps we cover next (tap the icons to navigate to the related section):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcloudinary-res.cloudinary.com%2Fimage%2Fupload%2Fv1698223367%2Fblog%2Fdjango_media_management.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcloudinary-res.cloudinary.com%2Fimage%2Fupload%2Fv1698223367%2Fblog%2Fdjango_media_management.png" width="800" height="217"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup and Configuration
&lt;/h2&gt;

&lt;p&gt;Before we dive into the specifics of media management with Python and Flask, you'll need to sign up for Cloudinary. It's quick and straightforward.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing and Configuring Cloudinary in Your Python/Flask App
&lt;/h3&gt;

&lt;p&gt;To get started with Cloudinary in your Python/Flask app, follow these steps:&lt;/p&gt;

&lt;p&gt;In your project virtualenv:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install the Cloudinary Python library using &lt;code&gt;pip install cloudinary&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure Cloudinary in your Flask application, typically inside app.py or a separate &lt;code&gt;config.py&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config.py or app.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cloudinary&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cloudinary.uploader&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cloudinary.api&lt;/span&gt;

&lt;span class="n"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;cloud_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_cloud_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_api_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;api_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_api_secret&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Replace &lt;code&gt;your_cloud_name&lt;/code&gt;, &lt;code&gt;your_api_key&lt;/code&gt;, and &lt;code&gt;your_api_secret&lt;/code&gt; with your actual Cloudinary credentials, which you can find on the &lt;a href="https://console.cloudinary.com/app/settings/api-keys" rel="noopener noreferrer"&gt;API keys&lt;/a&gt; page of the Console Settings.&lt;/p&gt;

&lt;ol start="3"&gt;&lt;li&gt;If you're using a &lt;code&gt;config.py&lt;/code&gt;, you'd load it in your Flask app like this:&lt;/li&gt;&lt;/ol&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;  &lt;span class="c1"&gt;# or your preferred config structure
&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these steps, you've successfully integrated Cloudinary into your Flask project, and you're ready to use its media management capabilities (upload, transform, search, deliver).&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhanced Media Processing With Upload Presets
&lt;/h2&gt;

&lt;p&gt;Upload presets in Cloudinary help you automate your media workflow by applying transformations, metadata rules, and upload behaviors the moment a file arrives. This means less processing in your Flask routes and far more consistent results across all your media.&lt;/p&gt;

&lt;p&gt;In this article, we’ll focus on two powerful capabilities you can unlock through presets: &lt;strong&gt;automated transformations&lt;/strong&gt; and &lt;strong&gt;auto-tagging&lt;/strong&gt;. You can explore many other preset options in Cloudinary’s &lt;a href="https://link.cloudinary.com/umWsU" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, but these two alone can dramatically simplify your media pipeline. &lt;/p&gt;

&lt;h2&gt;
  
  
  Automating Transformation Settings
&lt;/h2&gt;

&lt;p&gt;Upload presets allow you to automatically apply transformations such as resizing, cropping, recoloring, background removal, and more, without writing additional code in Flask. For example, you can create presets that remove backgrounds from uploaded logos, ensure all portfolio images are a consistent size, or sharpen older or lower-quality photos. &lt;/p&gt;

&lt;p&gt;Each preset applies its rules automatically when the file is uploaded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; By applying the &lt;a href="https://link.cloudinary.com/umWsW" rel="noopener noreferrer"&gt;g_auto&lt;/a&gt; AI-powered transformation, you can ensure that crops preserve the important parts of your images and keep the main subject of your videos in focus.&lt;/p&gt;

&lt;p&gt;By defining these settings once, every uploaded media file stays consistent and optimized with no manual editing or external image processing tools required.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fcloudinary-marketing%2Fimages%2Fw_500%2Ch_303%2Cc_scale%2Ff_auto%2Cq_auto%2Fv1698140107%2FWeb_Assets%2Fblog%2Frecolor%2Frecolor.gif%3F_i%3DAA" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fcloudinary-marketing%2Fimages%2Fw_500%2Ch_303%2Cc_scale%2Ff_auto%2Cq_auto%2Fv1698140107%2FWeb_Assets%2Fblog%2Frecolor%2Frecolor.gif%3F_i%3DAA" width="500" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Leveraging Auto-Tagging
&lt;/h3&gt;

&lt;p&gt;If you enable auto-tagging through Cloudinary’s categorization engines, images are automatically analyzed and tagged based on their content. These tags can support a variety of downstream tasks, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;organizing assets&lt;/li&gt;
&lt;li&gt;improving searchability&lt;/li&gt;
&lt;li&gt;enabling content discovery&lt;/li&gt;
&lt;li&gt;powering filters in your UI or internal tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, an image of a woman on a city street using a smartphone and carrying a bag might receive tags such as “woman,” “bag,” “mobile phone,” “purse,” or “car,” depending on the AI model you use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; To use auto-tagging, you’ll need to subscribe to one of Cloudinary’s &lt;a href="https://cloudinary.com/documentation/cloudinary_add_ons#auto_tagging" rel="noopener noreferrer"&gt;auto-tagging add-ons&lt;/a&gt;. The examples in this blog use the &lt;a href="https://link.cloudinary.com/umWsX" rel="noopener noreferrer"&gt;Rekognition Auto Tagging&lt;/a&gt; add-on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fcloudinary-marketing%2Fimages%2Ff_auto%2Cq_auto%2Fv1764259730%2FWeb_Assets%2Fblog%2Fwoman-business-suit%2Fwoman-business-suit.jpg%3F_i%3DAA" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fcloudinary-marketing%2Fimages%2Ff_auto%2Cq_auto%2Fv1764259730%2FWeb_Assets%2Fblog%2Fwoman-business-suit%2Fwoman-business-suit.jpg%3F_i%3DAA" width="760" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Red-haired woman in stylish outfit chatting on phone&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Check out the &lt;a href="https://link.cloudinary.com/umWsY" rel="noopener noreferrer"&gt;Computer Vision Image Analysis for Your E-commerce Website&lt;/a&gt; demo to see Cloudinary in action, returning information about the content it identifies in your images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating an Upload Preset
&lt;/h2&gt;

&lt;p&gt;You can create an upload preset using the Cloudinary Admin API right from your Flask application or a setup script. Here’s an example using the Python SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Define the upload preset details
&lt;/span&gt;&lt;span class="n"&gt;upload_preset_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my_preset&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;upload_preset_options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unsigned&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;folder&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my_folder&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my_tags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;transformation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;width&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;height&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;crop&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fill&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;effect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vignette&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;categorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;aws_rek_tagging&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto_tagging&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.9&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Create the upload preset using the SDK
&lt;/span&gt;&lt;span class="n"&gt;upload_preset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_upload_preset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;upload_preset_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;upload_preset_options&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Check if the upload preset was created successfully
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;upload_preset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;upload_preset_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Upload preset created successfully.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Failed to create upload preset.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once created, simply reference the preset name during upload, and all rules will apply automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Uploading Media With Python and Flask
&lt;/h2&gt;

&lt;p&gt;Uploading files is central to your Flask workflow, and Cloudinary provides two flexible methods: the Upload Widget for client-side uploads and server-side uploads from your Flask routes.&lt;/p&gt;

&lt;p&gt;Let’s explore both.&lt;/p&gt;

&lt;h3&gt;
  
  
  Upload Widget
&lt;/h3&gt;

&lt;p&gt;If your app accepts user-generated content (UGC), such as portfolio images from creators or product photos submitted by small business owners, the Cloudinary Upload Widget provides a smooth, reliable upload experience directly from the browser. You can attach any upload presets you’ve created so transformations, optimization, tagging, and other processing happen automatically on upload, without additional Flask code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; You can also create a preset that automatically &lt;a href="https://cloudinary.com/documentation/user_generated_content#moderate" rel="noopener noreferrer"&gt;moderates&lt;/a&gt; user-uploaded images and videos to help ensure that the content appearing on your site is appropriate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fcloudinary-marketing%2Fimages%2Ff_auto%2Cq_auto%2Fv1764260271%2FWeb_Assets%2Fblog%2Fupload_widget_accessible-1_3948189bcb%2Fupload_widget_accessible-1_3948189bcb.png%3F_i%3DAA" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fcloudinary-marketing%2Fimages%2Ff_auto%2Cq_auto%2Fv1764260271%2FWeb_Assets%2Fblog%2Fupload_widget_accessible-1_3948189bcb%2Fupload_widget_accessible-1_3948189bcb.png%3F_i%3DAA" width="1580" height="1220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: portfolio or catalog uploads
&lt;/h3&gt;

&lt;p&gt;A portfolio gallery or a simple shop catalog where users can upload photos or videos is an ideal scenario for the Upload Widget. It handles the entire client-side upload flow, while your presets manage all media processing in the background.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fc_scale%2Cw_500%2Fl_moon_layer%2Fc_scale%2Cw_150%2Ffl_layer_apply%2Cg_north_east%2Fl_text%3Aroboto_20_bold%3AMoonlight%2Ffl_layer_apply%2Cg_north_east%2Cx_30%2Cy_65%2Fcity_night_time.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fc_scale%2Cw_500%2Fl_moon_layer%2Fc_scale%2Cw_150%2Ffl_layer_apply%2Cg_north_east%2Fl_text%3Aroboto_20_bold%3AMoonlight%2Ffl_layer_apply%2Cg_north_east%2Cx_30%2Cy_65%2Fcity_night_time.jpg" width="500" height="750"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdemo-res.cloudinary.com%2Fw_500%2Fl_docs%3Awedding.jpg%2Cc_pad%2Cw_250%2Ch_250%2Fl_radialize%2Ffl_layer_apply%2Ce_displace%2Cy_-8%2Ffl_layer_apply%2Cx_10%2Cb_transparent%2Fleft_mug" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdemo-res.cloudinary.com%2Fw_500%2Fl_docs%3Awedding.jpg%2Cc_pad%2Cw_250%2Ch_250%2Fl_radialize%2Ffl_layer_apply%2Ce_displace%2Cy_-8%2Ffl_layer_apply%2Cx_10%2Cb_transparent%2Fleft_mug" width="500" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fv1489074100%2Fgirl_camera.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fv1489074100%2Fgirl_camera.jpg" width="800" height="1195"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Implement the Upload Widget in Flask
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create a route that renders the template:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/upload-media&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upload_media&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;upload_media.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol start="2"&gt;&lt;li&gt; Create the template (templates/upload_media.html):
&lt;/li&gt;&lt;/ol&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="n"&gt;DOCTYPE&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Media&lt;/span&gt; &lt;span class="n"&gt;Upload&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;upload_widget&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cloudinary-button&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Upload&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://upload-widget.cloudinary.com/global/all.js&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text/javascript&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  

    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text/javascript&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  
        &lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="n"&gt;myWidget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createUploadWidget&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="n"&gt;cloudName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my_cloud_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;uploadPreset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;my_preset&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;const&lt;/span&gt; &lt;span class="n"&gt;imageUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;secure_url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Done! Here is the file info: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Send&lt;/span&gt; &lt;span class="n"&gt;imageUrl&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt; &lt;span class="n"&gt;backend&lt;/span&gt;
                &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/save-media&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;imageUrl&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;upload_widget&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;click&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;myWidget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol start="3"&gt;&lt;li&gt; Add the URL rule (if not using decorators):
&lt;/li&gt;&lt;/ol&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app.py
&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_url_rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/upload-media&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;view_func&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;upload_media&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol start="4"&gt;&lt;li&gt; Test it by visiting:
&lt;/li&gt;&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;http://localhost:5000/upload-media&lt;/code&gt; (with your dev server running)&lt;/p&gt;

&lt;p&gt;You now have a complete client-side upload flow, with Cloudinary handling media processing automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server-Side Upload
&lt;/h2&gt;

&lt;p&gt;Server-side uploads are perfect when you need automation or want to process files uploaded through a traditional &lt;/p&gt;.
&lt;h3&gt;
  
  
  Example Use Case
&lt;/h3&gt;

&lt;p&gt;News aggregators, podcast platforms, or content curation apps often automate large-scale ingestion:&lt;/p&gt;
&lt;h1&gt;
  
  
  analyze images
&lt;/h1&gt;
&lt;h1&gt;
  
  
  apply tags
&lt;/h1&gt;
&lt;h1&gt;
  
  
  create captions
&lt;/h1&gt;
&lt;h1&gt;
  
  
  run moderation
&lt;/h1&gt;
&lt;h1&gt;
  
  
  convert formats
&lt;/h1&gt;

&lt;p&gt;Cloudinary can handle these tasks server-side with very little code.&lt;/p&gt;
&lt;h3&gt;
  
  
  Uploading From Flask
&lt;/h3&gt;


&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cloudinary.uploader&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/upload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;file_to_upload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uploader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;file_to_upload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;upload_preset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my_upload_preset&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;secure_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;And that’s it. Your media is uploaded, transformed, stored, and ready to deliver.&lt;/p&gt;
&lt;h2&gt;
  
  
  Searching Made Simple
&lt;/h2&gt;

&lt;p&gt;Once your assets are stored in Cloudinary, especially with auto-tagging enabled, searching becomes incredibly fast.&lt;/p&gt;

&lt;p&gt;Instead of digging through folders or writing custom queries, you can retrieve matching media with a single expression:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_media_by_tags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Search&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tags:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Example queries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;tags:portrait&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tags:handmade&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;resource_type:video&lt;/code&gt; AND &lt;code&gt;tags:nature&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use search to build quick admin dashboards, galleries, filters, or automated content workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delivering Your Media
&lt;/h2&gt;

&lt;p&gt;With Cloudinary managing your media, delivery becomes as powerful as upload. You can apply on-the-fly transformations directly in the image or video URL with no processing required in Flask.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Dynamic Image Sizing in a Flask Template
&lt;/h3&gt;



&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt; 
  &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{ cloudinary_url(public_id, width=width, height=height, crop=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fill&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;) }}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
  &lt;span class="n"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Your Image&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Or, using the SDK:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cloudinary.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cloudinary_url&lt;/span&gt;

&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cloudinary_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;public_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;crop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fill&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;h2&gt;
  
  
  Why this beats DIY storage
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;You don’t store multiple versions of each image&lt;/li&gt;
&lt;li&gt;Users automatically receive optimized formats (WebP, AVIF, MP4, etc.)&lt;/li&gt;
&lt;li&gt;Dynamic resizing keeps your site lightweight and fast&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For responsive layouts, check out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloudinary.com/documentation/responsive_html" rel="noopener noreferrer"&gt;Responsive images using HTML and dynamic image transformations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloudinary.com/documentation/video_overview" rel="noopener noreferrer"&gt;Managing and delivering videos at scale.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fcloudinary-marketing%2Fimages%2Ff_auto%2Cq_auto%2Fv1698149581%2FWeb_Assets%2Fblog%2Fguitar-man-1%2Fguitar-man-1.jpeg%3F_i%3DAA" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fcloudinary-marketing%2Fimages%2Ff_auto%2Cq_auto%2Fv1698149581%2FWeb_Assets%2Fblog%2Fguitar-man-1%2Fguitar-man-1.jpeg%3F_i%3DAA" width="1600" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fcloudinary-marketing%2Fimages%2Ff_auto%2Cq_auto%2Fv1698149585%2FWeb_Assets%2Fblog%2Fguitar-man2-1%2Fguitar-man2-1.jpeg%3F_i%3DAA" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fcloudinary-marketing%2Fimages%2Ff_auto%2Cq_auto%2Fv1698149585%2FWeb_Assets%2Fblog%2Fguitar-man2-1%2Fguitar-man2-1.jpeg%3F_i%3DAA" width="600" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fcloudinary-marketing%2Fimages%2Ff_auto%2Cq_auto%2Fv1698149587%2FWeb_Assets%2Fblog%2Fguitar-man3-1%2Fguitar-man3-1.jpeg%3F_i%3DAA" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fcloudinary-marketing%2Fimages%2Ff_auto%2Cq_auto%2Fv1698149587%2FWeb_Assets%2Fblog%2Fguitar-man3-1%2Fguitar-man3-1.jpeg%3F_i%3DAA" width="400" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap: Presets, Uploads, Search, and Delivery
&lt;/h2&gt;

&lt;p&gt;In short, the features we’ve covered include upload presets, smart uploads, AI-powered search, and dynamic delivery. Apply these features to take managing media files in Flask from “painful but necessary” to “effortless and scalable.”&lt;/p&gt;

&lt;p&gt;Instead of writing boilerplate logic for uploads, storage, and optimization, you can focus on building features your users care about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Search &amp;amp; discovery (keywords):&lt;/strong&gt; Flask file upload Cloudinary; Cloudinary upload preset Python; Cloudinary Search API tags; Cloudinary Upload Widget Flask; Flask image delivery and &lt;code&gt;cloudinary_url&lt;/code&gt; transformations; Python SDK, user uploads, UGC.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://link.cloudinary.com/umWs0" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>flask</category>
      <category>python</category>
      <category>media</category>
      <category>upload</category>
    </item>
    <item>
      <title>Build a Fast React Portfolio with Cloudinary Image and Video URLs</title>
      <dc:creator>Sharon Yelenik</dc:creator>
      <pubDate>Mon, 23 Mar 2026 18:13:17 +0000</pubDate>
      <link>https://dev.to/cloudinary/how-to-create-a-digital-portfolio-that-visually-pops-8li</link>
      <guid>https://dev.to/cloudinary/how-to-create-a-digital-portfolio-that-visually-pops-8li</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Excerpt:&lt;/strong&gt; A hands-on React + Vite portfolio uses Cloudinary URL transformations for crops, blur, and video—so you ship sharp media without hand-editing every asset. For developers job-hunting and replacing static PDF-only résumés.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Job searching can be tough, and so is standing out among the competition.&lt;/p&gt;

&lt;p&gt;This walkthrough uses a React + Vite sample portfolio and Cloudinary’s demo (then your) cloud to serve optimized images and video.&lt;/p&gt;

&lt;p&gt;When putting together job applications, there’s always that question: How should I describe myself? Will potential employers care more about past experience or a list of skills? A digital portfolio answers that question in a way a résumé alone can’t. It shows what you’re actually capable of.&lt;/p&gt;

&lt;p&gt;That’s why I put together this digital portfolio demo project.&lt;/p&gt;

&lt;p&gt;Instead of talking about performance, polish, and visual quality in theory, I wanted to demonstrate what that looks like in practice. This portfolio is built the way I’d recommend anyone build one today: fast, visually sharp, and optimized from the start.&lt;/p&gt;

&lt;p&gt;In this guide, I’ll walk you through how I build a frontend portfolio project using Cloudinary to handle all the image and video magic. No endless hours in Photoshop. No massive file sizes. And, no manual resizing for every device.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you’ll learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Clone and run this repo locally: &lt;a href="https://github.com/cloudinary-devs/digital_portfolio" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Swap the &lt;code&gt;demo&lt;/code&gt; cloud for your own Cloudinary cloud name&lt;/li&gt;
&lt;li&gt;Read transformation URLs for face crop, hero blur, and grid fill&lt;/li&gt;
&lt;li&gt;Use video &lt;code&gt;so_&lt;/code&gt; / &lt;code&gt;c_pad&lt;/code&gt; parameters in delivery URLs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Live Demo (Optional, but Highly Recommended)
&lt;/h2&gt;

&lt;p&gt;Before diving into the code, you can check out the live portfolio demo &lt;a href="https://github.com/cloudinary-devs/digital_portfolio" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Feel free to explore it, then come back and see how everything works under the hood.&lt;/p&gt;


  
  Your browser does not support the video tag.


&lt;h2&gt;
  
  
  What “stands out” means in this repo
&lt;/h2&gt;

&lt;p&gt;Building a great-looking digital portfolio is a no-brainer. However, the real question is: How do you make yours stand out? One of the biggest differentiators is focusing on performance and visual polish. When your portfolio feels fast, smooth, and thoughtfully built, it immediately comes across as more professional. &lt;/p&gt;

&lt;p&gt;And when you build it efficiently, you’re also signaling to future employers that you know how to work efficiently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deliver images that load fast but look crisp.&lt;/li&gt;
&lt;li&gt;Make videos that play smoothly without eating bandwidth.&lt;/li&gt;
&lt;li&gt;Create responsive layouts that look perfect on every device.&lt;/li&gt;
&lt;li&gt;Apply visual effects that make content stand out.&lt;/li&gt;
&lt;li&gt;Optimize everything without sacrificing quality.&lt;/li&gt;
&lt;li&gt;Treating the portfolio as a product ships the same skills you use on the job.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The tech stack: React, Vite, CSS, and Cloudinary
&lt;/h2&gt;

&lt;p&gt;For my portfolio, I went with tools that are popular in the industry and honestly just fun to work with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React 19 with TypeScript for type safety and component architecture.&lt;/li&gt;
&lt;li&gt;Vite for lightning-fast development and optimized builds.&lt;/li&gt;
&lt;li&gt;CSS for beautiful, responsive styling.&lt;/li&gt;
&lt;li&gt;Cloudinary for all image and video transformations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to &lt;a href="https://github.com/cloudinary-devs/digital_portfolio" rel="noopener noreferrer"&gt;clone my code&lt;/a&gt; and adapt it to whatever you’re used to working with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;I’m sharing my portfolio with you as a starting point. Once you get a feel for how it works, you can customize the design to match your style and add sections that show off what matters to you.&lt;/p&gt;

&lt;p&gt;From the repo root (after cloning from Cloudinary’s org):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone the starter portfolio repo&lt;/span&gt;
git clone https://github.com/cloudinary-devs/digital_portfolio.git

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;digital_portfolio
npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Start the development server&lt;/span&gt;
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’m excited to see how you make it your own.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Cloudinary Helps Meet Stand-Out Goals
&lt;/h2&gt;

&lt;p&gt;A portfolio that stands out needs to be fast, visually sharp, and responsive across devices.&lt;/p&gt;

&lt;p&gt;Without automation, that usually means resizing images manually, generating multiple breakpoints, compressing files carefully, and managing large video assets.&lt;/p&gt;

&lt;p&gt;Cloudinary handles image and video delivery, optimization, and transformations through simple URL parameters. In this project, cropping, resizing, blur effects, format conversion, and quality optimization are all applied directly in the media URLs.&lt;/p&gt;

&lt;p&gt;Transformations run on the fly, and the right size and format are delivered automatically for each device and browser.&lt;/p&gt;

&lt;p&gt;Instead of maintaining multiple asset versions or editing files manually, I define the transformation once and move on, without sacrificing quality or performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Swap the Demo Media for Your Own
&lt;/h2&gt;

&lt;p&gt;This project uses Cloudinary’s demo account (&lt;code&gt;res.cloudinary.com/demo&lt;/code&gt;) with sample images and videos, so it works out of the box. When you’re ready, switch to your own Cloudinary account to display your own images and videos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a Cloudinary Account
&lt;/h3&gt;

&lt;p&gt;Sign up for a &lt;a href="https://link.cloudinary.com/umWb1" rel="noopener noreferrer"&gt;free Cloudinary account&lt;/a&gt; (the free tier is more than enough for a portfolio).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Find Your Cloud Name
&lt;/h3&gt;

&lt;p&gt;After logging in, copy your cloud name from the dashboard. You’ll use it in URLs like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://res.cloudinary.com/&amp;lt;your_cloud_name&amp;gt;/image/upload/&amp;lt;public_id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Update One Line in the Code
&lt;/h3&gt;

&lt;p&gt;In your project, change this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Set this to your cloud name when you're ready to use your own media&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CLOUDINARY_CLOUD_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;demo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…to your cloud name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CLOUDINARY_CLOUD_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;your_cloud_name&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it. Since CLOUDINARY_BASE is built from CLOUDINARY_CLOUD_NAME, all image/video URLs that use CLOUDINARY_BASE will automatically point to your account.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Upload Your Own Media and Swap the Public IDs
&lt;/h3&gt;

&lt;p&gt;In your code, you reference assets using public IDs — for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;docs/profile-pic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means Cloudinary is looking for an asset with the public ID &lt;code&gt;docs/catwalk&lt;/code&gt; in your cloud.&lt;/p&gt;

&lt;p&gt;After you upload your own images/videos to Cloudinary, replace those image values with your own public IDs, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;portfolio/catwalk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You don’t need to change the transformations. Everything in the URL after &lt;code&gt;/upload/&lt;/code&gt; (like &lt;code&gt;c_fill,g_auto,h_400,w_600/f_auto/q_auto&lt;/code&gt;) can stay the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cool Cloudinary Features I Used
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Smart Cropping With Face Detection
&lt;/h3&gt;

&lt;p&gt;For the testimonials section, I needed consistent circular profile images that focused tightly on each person’s face.&lt;/p&gt;

&lt;p&gt;Here’s the original image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fdocs%2Fprofile-pic.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fdocs%2Fprofile-pic.jpg" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://res.cloudinary.com/demo/image/upload/" rel="noopener noreferrer"&gt;https://res.cloudinary.com/demo/image/upload/&lt;/a&gt;&lt;br&gt;
docs/profile-pic.jpg&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And here’s the transformed version:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fc_thumb%2Cg_face%2Ch_300%2Cw_300%2Fr_max%2Fe_sharpen%3A80%2Ff_auto%2Fq_auto%2Fdocs%2Fprofile-pic.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fc_thumb%2Cg_face%2Ch_300%2Cw_300%2Fr_max%2Fe_sharpen%3A80%2Ff_auto%2Fq_auto%2Fdocs%2Fprofile-pic.jpg" width="300" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://res.cloudinary.com/demo/image/upload/c_thumb,g_face,h_300,w_300/r_max/e_sharpen:80/f_auto/q_auto/docs/profile-pic.jpg" rel="noopener noreferrer"&gt;https://res.cloudinary.com/demo/image/upload/c_thumb,g_face,h_300,w_300/r_max/e_sharpen:80/f_auto/q_auto/docs/profile-pic.jpg&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  What the Transformation Does
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;c_thumb,g_face&lt;/code&gt; automatically detects the face and crops around it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;h_300,w_300&lt;/code&gt; enforces a fixed square size.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;r_max&lt;/code&gt; makes the image circular.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;e_sharpen:80&lt;/code&gt; restores clarity after resizing.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;f_auto,q_auto&lt;/code&gt; handle format and compression.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result isn’t just a circle. It’s a consistent 300×300 headshot, centered correctly every time — regardless of how the original photo was framed.&lt;/p&gt;

&lt;p&gt;That means no manual cropping, guessing focal points, or layout inconsistencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Hero Background Blur
&lt;/h3&gt;

&lt;p&gt;For the hero section, I wanted a full-width background image that wouldn’t compete with the foreground content.&lt;/p&gt;

&lt;p&gt;Original image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fvieste_italy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fvieste_italy.jpg" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://res.cloudinary.com/demo/image/upload/vieste_italy.jpg" rel="noopener noreferrer"&gt;https://res.cloudinary.com/demo/image/upload/vieste_italy.jpg&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Transformed version:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fc_fill%2Cg_auto%2Ch_1080%2Cw_1920%2Fe_blur%3A800%2Ff_auto%2Fq_auto%2Fvieste_italy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fc_fill%2Cg_auto%2Ch_1080%2Cw_1920%2Fe_blur%3A800%2Ff_auto%2Fq_auto%2Fvieste_italy.jpg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://res.cloudinary.com/demo/image/upload/c_fill,g_auto,h_1080,w_1920/e_blur:800/f_auto/q_auto/vieste_italy.jpg" rel="noopener noreferrer"&gt;https://res.cloudinary.com/demo/image/upload/c_fill,g_auto,h_1080,w_1920/e_blur:800/f_auto/q_auto/vieste_italy.jpg&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  What the Transformation Does
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;c_fill,g_auto&lt;/code&gt; crops intelligently to 1920×1080.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;e_blur:800&lt;/code&gt; applies a strong blur effect.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;f_auto,q_auto&lt;/code&gt; optimize delivery.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The original image is detailed and high contrast — great for photography, not ideal for text overlays.&lt;/p&gt;

&lt;p&gt;By blurring it at delivery time, I keep the color and atmosphere while removing visual noise. The background supports the content instead of competing with it. No separate “blurred copy” of the file is needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. AI-Enhanced Portrait
&lt;/h3&gt;

&lt;p&gt;For the hero portrait, I wanted a clean, high-quality look — even if the source image wasn’t studio-perfect.&lt;/p&gt;

&lt;p&gt;Original:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fdocs%2Fprofile-pic1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fdocs%2Fprofile-pic1.jpg" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://res.cloudinary.com/demo/image/upload/docs/profile-pic1.jpg" rel="noopener noreferrer"&gt;https://res.cloudinary.com/demo/image/upload/docs/profile-pic1.jpg&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Transformed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fc_fill%2Cg_face%2Ch_300%2Cw_300%2Fr_max%2Cbo_2px_solid_green%2Fe_enhance%2Ff_auto%2Fq_auto%3Abest%2Fdocs%2Fprofile-pic1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fc_fill%2Cg_face%2Ch_300%2Cw_300%2Fr_max%2Cbo_2px_solid_green%2Fe_enhance%2Ff_auto%2Fq_auto%3Abest%2Fdocs%2Fprofile-pic1.jpg" width="304" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://res.cloudinary.com/demo/image/upload/c_fill,g_face,h_300,w_300/r_max,bo_2px_solid_green/e_enhance/f_auto/q_auto:best/docs/profile-pic1.jpg" rel="noopener noreferrer"&gt;https://res.cloudinary.com/demo/image/upload/c_fill,g_face,h_300,w_300/r_max,bo_2px_solid_green/e_enhance/f_auto/q_auto:best/docs/profile-pic1.jpg&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  What the Transformation Does
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;g_face&lt;/code&gt; centers the subject.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;r_max&lt;/code&gt; applies a circular crop.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bo_2px_solid_green&lt;/code&gt; adds a clean, circular border.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;e_enhance&lt;/code&gt; enhances lighting and contrast using AI.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;q_auto:best&lt;/code&gt; balances compression with quality.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The enhancement isn’t dramatic — it’s subtle. Skin tones are more balanced, contrast is cleaner, and the framing is consistent.&lt;/p&gt;

&lt;p&gt;It looks like a designed component, not just an uploaded image.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Responsive Project Images
&lt;/h3&gt;

&lt;p&gt;In my project grid, the source images came from different industries — fashion, e-commerce, outdoor photography — all with different aspect ratios.&lt;/p&gt;

&lt;p&gt;Here’s one of the original images:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fwoman_mountain_ledge.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fwoman_mountain_ledge.jpg" width="800" height="1059"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://res.cloudinary.com/demo/image/upload/woman_mountain_ledge.jpg" rel="noopener noreferrer"&gt;https://res.cloudinary.com/demo/image/upload/woman_mountain_ledge.jpg&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And here’s the version used in the grid:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fc_fill%2Cg_auto%2Ch_400%2Cw_600%2Fr_20%2Fe_saturation%3A20%2Ff_auto%2Fq_auto%2Fwoman_mountain_ledge.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fc_fill%2Cg_auto%2Ch_400%2Cw_600%2Fr_20%2Fe_saturation%3A20%2Ff_auto%2Fq_auto%2Fwoman_mountain_ledge.jpg" width="600" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://res.cloudinary.com/demo/image/upload/c_fill,g_auto,h_400,w_600/r_20/e_saturation:20/f_auto/q_auto/woman_mountain_ledge.jpg" rel="noopener noreferrer"&gt;https://res.cloudinary.com/demo/image/upload/c_fill,g_auto,h_400,w_600/r_20/e_saturation:20/f_auto/q_auto/woman_mountain_ledge.jpg&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  What the Transformation Does
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;c_fill,h_400,w_600&lt;/code&gt; forces a consistent 600×400 frame.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;g_auto&lt;/code&gt; intelligently selects the focal area.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;r_20&lt;/code&gt; adds rounded corners.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;e_saturation:20&lt;/code&gt; slightly boosts color.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;f_auto,q_auto&lt;/code&gt; optimize delivery.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The original image has its own natural proportions.&lt;/p&gt;

&lt;p&gt;The transformed version guarantees:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every card in the grid is exactly the same size.&lt;/li&gt;
&lt;li&gt;No distortion.&lt;/li&gt;
&lt;li&gt;No manual cropping.&lt;/li&gt;
&lt;li&gt;No awkward whitespace.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even though the source images vary wildly, the layout stays predictable and clean. That’s what makes the grid feel cohesive.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Video trims and pads via URL parameters
&lt;/h3&gt;

&lt;p&gt;Video is usually where portfolios fall apart. Files are large, aspect ratios are inconsistent, and playback isn’t optimized.&lt;/p&gt;

&lt;p&gt;Here’s the original full video:&lt;/p&gt;


  
  Your browser does not support the video tag.


&lt;p&gt;&lt;em&gt;&lt;a href="https://res.cloudinary.com/demo/video/upload/v1731855790/guy_woman_mobile.mp4" rel="noopener noreferrer"&gt;https://res.cloudinary.com/demo/video/upload/v1731855790/guy_woman_mobile.mp4&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And here’s the version used in the portfolio:&lt;/p&gt;


  
  Your browser does not support the video tag.


&lt;p&gt;&lt;em&gt;&lt;a href="https://res.cloudinary.com/demo/video/upload/so_0.5,eo_2.5/c_pad,h_400,w_600/b_rgb:d4a520/v1731855790/guy_woman_mobile.mp4" rel="noopener noreferrer"&gt;https://res.cloudinary.com/demo/video/upload/so_0.5,eo_2.5/c_pad,h_400,w_600/b_rgb:d4a520/v1731855790/guy_woman_mobile.mp4&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  What the Transformation Does
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;so_0.5,eo_2.5&lt;/code&gt; trims the clip to a specific segment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;c_pad,h_400,w_600&lt;/code&gt; fits it into a 600×400 frame without cutting off content.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;b_rgb:d4a520&lt;/code&gt; fills extra space with a consistent background color.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;f_auto,q_auto&lt;/code&gt; optimize format and compression.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of uploading a separately edited clip, I trim and resize at delivery time.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The video is shorter and lighter.&lt;/li&gt;
&lt;li&gt;The layout dimensions are guaranteed.&lt;/li&gt;
&lt;li&gt;There are no black bars.&lt;/li&gt;
&lt;li&gt;The browser gets the best possible format automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It behaves like a designed component — not a raw media file dropped onto a page.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Small Details That Make a Difference
&lt;/h3&gt;

&lt;p&gt;Once layout and performance were handled, I added subtle refinements.&lt;/p&gt;

&lt;p&gt;Here’s the original image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fdocs%2Fprofile-pic1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fdocs%2Fprofile-pic1.jpg" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://res.cloudinary.com/demo/image/upload/docs/profile-pic1.jpg" rel="noopener noreferrer"&gt;https://res.cloudinary.com/demo/image/upload/docs/profile-pic1.jpg&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And here’s the polished version:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fc_fill%2Cg_auto%2Ch_800%2Cw_700%2Fe_vignette%3A30%2Fe_sharpen%3A100%2Fr_20%2Fbo_1px_solid_rgb%3Ae0e0e0%2Ff_auto%2Fq_auto%2Fdocs%2Fprofile-pic1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdemo%2Fimage%2Fupload%2Fc_fill%2Cg_auto%2Ch_800%2Cw_700%2Fe_vignette%3A30%2Fe_sharpen%3A100%2Fr_20%2Fbo_1px_solid_rgb%3Ae0e0e0%2Ff_auto%2Fq_auto%2Fdocs%2Fprofile-pic1.jpg" width="702" height="802"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://res.cloudinary.com/demo/image/upload/c_fill,g_auto,h_800,w_700/e_vignette:30/e_sharpen:100/r_20/bo_1px_solid_rgb:e0e0e0/f_auto/q_auto/docs/profile-pic1.jpg" rel="noopener noreferrer"&gt;https://res.cloudinary.com/demo/image/upload/c_fill,g_auto,h_800,w_700/e_vignette:30/e_sharpen:100/r_20/bo_1px_solid_rgb:e0e0e0/f_auto/q_auto/docs/profile-pic1.jpg&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  What the Transformation Does
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;c_fill,g_auto&lt;/code&gt; enforces consistent framing.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;e_vignette:30&lt;/code&gt; darkens the edges slightly.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;e_sharpen:100&lt;/code&gt; restores clarity.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;r_20&lt;/code&gt; rounds the corners.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bo_1px_solid_rgb:e0e0e0&lt;/code&gt; adds a subtle border.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;f_auto,q_auto&lt;/code&gt; optimize delivery.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these effects are dramatic, but together they:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improve edge definition.&lt;/li&gt;
&lt;li&gt;Add separation from the background.&lt;/li&gt;
&lt;li&gt;Standardize presentation across sections.&lt;/li&gt;
&lt;li&gt;These are small adjustments, but they’re the difference between “image placed on a page” and “designed component.”&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  What This Means for Performance
&lt;/h4&gt;

&lt;p&gt;These effects are applied at the CDN, so the browser often receives smaller, correctly sized files than a raw upload.&lt;/p&gt;

&lt;p&gt;With Cloudinary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;70-80% smaller file sizes&lt;/strong&gt; compared to unoptimized images.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3-5x faster loading&lt;/strong&gt; thanks to automatic optimization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero manual editing time&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic device optimization&lt;/strong&gt; — phone users get mobile-sized images, desktop users get high-res.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When someone views your portfolio on their phone, they automatically get perfectly-sized images. On a 4K monitor, they get crisp, detailed versions. It just works.&lt;/p&gt;

&lt;p&gt;Notice how much this image was optimized and what that means for your website stats and loading time! Reduced from a 21.30 MB JPG to a 18.26 KB AVIF.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcloudinary-res.cloudinary.com%2Fimage%2Fupload%2Fv1771198908%2Fblog%2Fdigital_portfolio_mi_img.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcloudinary-res.cloudinary.com%2Fimage%2Fupload%2Fv1771198908%2Fblog%2Fdigital_portfolio_mi_img.png" width="800" height="1285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bringing It Full Circle
&lt;/h2&gt;

&lt;p&gt;Here’s what I love about this whole process: The skills you use to build an impressive portfolio are the same skills you’ll use every day in your job.&lt;/p&gt;

&lt;p&gt;When you build this portfolio, you’re learning how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build modern React applications with TypeScript.&lt;/li&gt;
&lt;li&gt;Create responsive, mobile-first designs.&lt;/li&gt;
&lt;li&gt;Optimize images and videos for real-world performance.&lt;/li&gt;
&lt;li&gt;Use cloud services to solve practical problems.&lt;/li&gt;
&lt;li&gt;Write clean, maintainable code.&lt;/li&gt;
&lt;li&gt;Think about user experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your portfolio becomes a preview of what you can do. So, you’ve shown you can build websites that look great, load fast, and feel professional. That’s exactly what teams are looking for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Making a portfolio that stands out doesn’t have to be complicated or stressful. It’s really about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Polish that shows you care about details.&lt;/li&gt;
&lt;li&gt;Performance that respects people’s time.&lt;/li&gt;
&lt;li&gt;Smart visual choices that guide the eye.&lt;/li&gt;
&lt;li&gt;Responsive design that works everywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using the right tools (like Cloudinary) to make your life easier.&lt;br&gt;
If you’re job searching right now, I hope this helps. You’ve got this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live demo&lt;/strong&gt;: &lt;a href="https://stackblitz.com/github/cloudinary-devs/digital_portfolio?file=README.md" rel="noopener noreferrer"&gt;https://stackblitz.com/github/cloudinary-devs/digital_portfolio?file=README.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source code&lt;/strong&gt;: &lt;a href="https://github.com/cloudinary-devs/digital_portfolio" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudinary docs&lt;/strong&gt;: &lt;a href="https://link.cloudinary.com/umWb2" rel="noopener noreferrer"&gt;https://cloudinary.com/documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://link.cloudinary.com/umWb1" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>react</category>
      <category>vite</category>
      <category>typescript</category>
      <category>portfolio</category>
    </item>
    <item>
      <title>Improve Web Performance: Image Formats, Responsive Images, and Core Web Vitals</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Wed, 11 Mar 2026 18:43:45 +0000</pubDate>
      <link>https://dev.to/cloudinary/your-images-are-probably-slowing-down-your-website-heres-how-to-fix-it-23je</link>
      <guid>https://dev.to/cloudinary/your-images-are-probably-slowing-down-your-website-heres-how-to-fix-it-23je</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Images usually dominate page weight. This guide covers formats (WebP/AVIF), responsive &lt;code&gt;srcset&lt;/code&gt;, lazy loading rules, and &lt;strong&gt;Core Web Vitals&lt;/strong&gt; for developers shipping production sites.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What you’ll get
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;mental model&lt;/strong&gt;: how &lt;strong&gt;bytes&lt;/strong&gt; (file size) relate to what you &lt;em&gt;see&lt;/em&gt; (pixels on screen) and to &lt;strong&gt;LCP&lt;/strong&gt; / &lt;strong&gt;CLS&lt;/strong&gt; / &lt;strong&gt;INP&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;format cheat sheet&lt;/strong&gt; (photos, UI, icons, fallbacks) and when &lt;strong&gt;lossy&lt;/strong&gt; vs &lt;strong&gt;lossless&lt;/strong&gt; makes sense.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When to lazy-load&lt;/strong&gt; (and when &lt;strong&gt;not&lt;/strong&gt; to—so you don’t hurt LCP).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Three fixes to try first&lt;/strong&gt; on a real page (largest image, format mix, mobile percentiles).
&lt;/li&gt;
&lt;li&gt;How to use &lt;strong&gt;Cloudinary’s Web Speed Test&lt;/strong&gt; to turn gut feel into numbers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The hidden performance problem on most websites
&lt;/h2&gt;

&lt;p&gt;This article explains how &lt;strong&gt;image bytes&lt;/strong&gt; affect &lt;strong&gt;LCP&lt;/strong&gt;, &lt;strong&gt;CLS&lt;/strong&gt;, and &lt;strong&gt;INP&lt;/strong&gt;, and what to change first.&lt;/p&gt;

&lt;p&gt;Open Chrome DevTools on almost any modern site and check the &lt;strong&gt;Network tab&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What you’ll usually see is something surprising:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Images dominate the payload.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The average homepage ships &lt;strong&gt;around 1 MB of images&lt;/strong&gt;, often &lt;strong&gt;more than JavaScript, CSS, and HTML combined&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.thoughtindustries.com%2Fcourse-uploads%2F4338ce4e-f809-4f5a-80f4-1d317c4a390d%2Fj0bb7waszgtt-Screenshot2025-11-25at12.31.09PM.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.thoughtindustries.com%2Fcourse-uploads%2F4338ce4e-f809-4f5a-80f4-1d317c4a390d%2Fj0bb7waszgtt-Screenshot2025-11-25at12.31.09PM.png" alt="watches in a web site" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A super heavy page with a lot of big product images. Lazy loading and optimization would help here.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That product page with 20 images?&lt;br&gt;
Each one is an opportunity to either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deliver a fast, polished experience&lt;/li&gt;
&lt;li&gt;Or frustrate users with slow loading and layout shifts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you care about &lt;strong&gt;Core Web Vitals, SEO, and user experience&lt;/strong&gt;, image optimization is one of the &lt;strong&gt;highest-impact improvements you can make&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article was generated from our &lt;a href="https://link.cloudinary.com/umq4g" rel="noopener noreferrer"&gt;Cloud to Crowd: Media IQ with Next.js&lt;/a&gt; curriculum, built by Cloudinary Developer Relations. Go through this curriculum and pass the certification exam to qualify to become an official &lt;a href="https://link.cloudinary.com/umq4h" rel="noopener noreferrer"&gt;Cloudinary Creator&lt;/a&gt;!&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Why images matter (and when they don’t)
&lt;/h2&gt;

&lt;p&gt;Humans process images much faster than text.&lt;/p&gt;

&lt;p&gt;A well-chosen image can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explain a product instantly&lt;/li&gt;
&lt;li&gt;Replace paragraphs of documentation&lt;/li&gt;
&lt;li&gt;Capture attention in fast-scrolling feeds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But images can also &lt;strong&gt;hurt your product experience&lt;/strong&gt; when used poorly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Too many images slow down page loads&lt;/li&gt;
&lt;li&gt;Poor cropping hides important information&lt;/li&gt;
&lt;li&gt;Large files damage &lt;strong&gt;Core Web Vitals&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Undisclosed AI or edited images reduce user trust&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So you need to follow this simple rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Right format + right size + smart delivery&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F41mtd49vkcucf25n7lid.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F41mtd49vkcucf25n7lid.png" alt="Images in a correct format" width="800" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check out the way these images are delivered to the browser&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Images and Core Web Vitals
&lt;/h2&gt;

&lt;p&gt;Images heavily influence Google's &lt;strong&gt;Core Web Vitals&lt;/strong&gt;, particularly:&lt;/p&gt;
&lt;h3&gt;
  
  
  Largest Contentful Paint (LCP)
&lt;/h3&gt;

&lt;p&gt;Measures how long it takes for the &lt;strong&gt;largest visible element&lt;/strong&gt; on the page to load.&lt;/p&gt;

&lt;p&gt;This is often your &lt;strong&gt;hero image&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A good LCP target is under 2.5 seconds.&lt;/p&gt;
&lt;h3&gt;
  
  
  Cumulative Layout Shift (CLS)
&lt;/h3&gt;

&lt;p&gt;Images without defined dimensions can cause layout shifts while loading.&lt;/p&gt;
&lt;h3&gt;
  
  
  Interaction to Next Paint (INP)
&lt;/h3&gt;

&lt;p&gt;Heavy pages delay responsiveness.&lt;/p&gt;

&lt;p&gt;Optimizing images can dramatically improve &lt;strong&gt;all three metrics&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  The current state of images on the web
&lt;/h2&gt;

&lt;p&gt;Nearly &lt;strong&gt;every webpage (99.9%) loads at least one image&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Average number of images per page:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Device&lt;/th&gt;
&lt;th&gt;Avg Images&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Desktop&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mobile&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Common formats used across the web:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Usage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JPEG&lt;/td&gt;
&lt;td&gt;32.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PNG&lt;/td&gt;
&lt;td&gt;28.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GIF&lt;/td&gt;
&lt;td&gt;16.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebP&lt;/td&gt;
&lt;td&gt;12%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SVG&lt;/td&gt;
&lt;td&gt;6.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AVIF&lt;/td&gt;
&lt;td&gt;1%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;But these numbers reflect &lt;strong&gt;habit, not best practices&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Performance-focused teams are already shifting toward modern formats.&lt;/p&gt;

&lt;p&gt;Example data from optimized platforms shows:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;Usage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;WebP&lt;/td&gt;
&lt;td&gt;49.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JPEG&lt;/td&gt;
&lt;td&gt;19.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AVIF&lt;/td&gt;
&lt;td&gt;13.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HEIC / PNG&lt;/td&gt;
&lt;td&gt;8.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The takeaway:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Modern image formats are replacing JPEG-first strategies.&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Choosing the right image format
&lt;/h2&gt;

&lt;p&gt;Different formats solve different problems.&lt;/p&gt;
&lt;h3&gt;
  
  
  Lossy compression
&lt;/h3&gt;

&lt;p&gt;Reduces file size by permanently discarding some data.&lt;/p&gt;

&lt;p&gt;Formats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JPEG&lt;/li&gt;
&lt;li&gt;WebP&lt;/li&gt;
&lt;li&gt;AVIF&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Much smaller file sizes&lt;/li&gt;
&lt;li&gt;Ideal for photos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slight quality loss&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  Lossless compression
&lt;/h3&gt;

&lt;p&gt;Reduces size &lt;strong&gt;without losing data&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Formats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PNG&lt;/li&gt;
&lt;li&gt;SVG&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perfect image quality&lt;/li&gt;
&lt;li&gt;Ideal for UI graphics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Larger file sizes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7tu5adjaeuss92qfydpv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7tu5adjaeuss92qfydpv.png" alt="quality varying in images" width="800" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check out the difference between a lower res image vs JPEG XL&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h3&gt;
  
  
  Quick developer cheat sheet
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;th&gt;Best format&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Photos&lt;/td&gt;
&lt;td&gt;WebP / AVIF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Graphics&lt;/td&gt;
&lt;td&gt;PNG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Icons&lt;/td&gt;
&lt;td&gt;SVG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legacy fallback&lt;/td&gt;
&lt;td&gt;JPEG&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Modern formats like &lt;strong&gt;WebP and AVIF can reduce file size by 40–60% compared to JPEG&lt;/strong&gt; while keeping similar visual quality.&lt;/p&gt;


&lt;h2&gt;
  
  
  Delivering images efficiently
&lt;/h2&gt;

&lt;p&gt;Choosing the right format is only part of the story.&lt;br&gt;
Delivery strategy matters just as much.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cloudinary&lt;/strong&gt; can automate &lt;strong&gt;format and size&lt;/strong&gt; selection for delivery so you’re not hand-tuning every URL. It helps you &lt;strong&gt;optimize, transform, and deliver&lt;/strong&gt; images for your users. See how on the &lt;a href="https://link.cloudinary.com/umq4i" rel="noopener noreferrer"&gt;Developers Hub&lt;/a&gt;.&lt;/p&gt;


&lt;h3&gt;
  
  
  1. Use responsive images
&lt;/h3&gt;

&lt;p&gt;Don't force mobile users to download desktop-sized images.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;srcset&lt;/code&gt; and &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;picture&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"hero.avif"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/avif"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"hero.webp"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"hero.jpg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Product hero image"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser automatically chooses the &lt;strong&gt;best supported format and size&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Use a CDN
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Content Delivery Network (CDN)&lt;/strong&gt; reduces the physical distance between your users and your images.&lt;/p&gt;

&lt;p&gt;Modern image CDNs can also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Resize images automatically&lt;/li&gt;
&lt;li&gt;Convert formats (WebP / AVIF)&lt;/li&gt;
&lt;li&gt;Adjust compression&lt;/li&gt;
&lt;li&gt;Crop dynamically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows you to store &lt;strong&gt;one high-quality original&lt;/strong&gt; while serving optimized versions.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Lazy-load images (strategically)
&lt;/h3&gt;

&lt;p&gt;Lazy loading delays images until they approach the viewport.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"gallery.jpg"&lt;/span&gt; &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Gallery image"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there is one important rule:&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Never lazy-load above-the-fold images.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Doing so hurts &lt;strong&gt;Largest Contentful Paint&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Lazy loading should only be used for &lt;strong&gt;below-the-fold content&lt;/strong&gt;. Do not lazy-load the &lt;strong&gt;hero&lt;/strong&gt; or other &lt;strong&gt;above-the-fold&lt;/strong&gt; &lt;strong&gt;LCP&lt;/strong&gt; candidates.&lt;/p&gt;




&lt;h2&gt;
  
  
  Responsible image usage
&lt;/h2&gt;

&lt;p&gt;Performance isn't the only consideration.&lt;/p&gt;

&lt;p&gt;Developers should also use images &lt;strong&gt;ethically and accessibly&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Avoid misleading visuals
&lt;/h3&gt;

&lt;p&gt;Don't use edited or cropped images that misrepresent a product.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Disclose AI-generated images
&lt;/h3&gt;

&lt;p&gt;Transparency builds trust with users.&lt;br&gt;
The banner at the top of this article was generated using Nano Banana.&lt;/p&gt;


&lt;h3&gt;
  
  
  3. Make images accessible
&lt;/h3&gt;

&lt;p&gt;Always include descriptive alt text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"dashboard.png"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Analytics dashboard showing user growth trends"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. Respect privacy and licensing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Check image licenses&lt;/li&gt;
&lt;li&gt;Get consent when photographing people&lt;/li&gt;
&lt;li&gt;Remove sensitive EXIF metadata&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  5. Balance aesthetics and performance
&lt;/h3&gt;

&lt;p&gt;A beautiful image that takes &lt;strong&gt;8 seconds to load&lt;/strong&gt; isn't beautiful.&lt;/p&gt;

&lt;p&gt;It's broken.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common image optimization myths
&lt;/h2&gt;

&lt;h3&gt;
  
  
  “A CDN replaces responsive images”
&lt;/h3&gt;

&lt;p&gt;False.&lt;/p&gt;

&lt;p&gt;Responsive images help the &lt;strong&gt;browser choose the correct size&lt;/strong&gt;, while CDNs deliver optimized assets.&lt;/p&gt;

&lt;p&gt;You need &lt;strong&gt;both&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  “More images mean more engagement”
&lt;/h3&gt;

&lt;p&gt;Not necessarily.&lt;/p&gt;

&lt;p&gt;Images should &lt;strong&gt;help users make decisions&lt;/strong&gt;, not clutter the interface.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three practical tips for developers
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Start with your largest image
&lt;/h3&gt;

&lt;p&gt;Open &lt;strong&gt;DevTools → Network&lt;/strong&gt; and find the biggest image.&lt;/p&gt;

&lt;p&gt;Check if delivered size == display size.&lt;/p&gt;

&lt;p&gt;A common mistake: 3000px image displayed in a 600px container.&lt;/p&gt;

&lt;p&gt;That’s wasted bandwidth.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Measure your format mix
&lt;/h3&gt;

&lt;p&gt;Check what formats your pipeline actually delivers.&lt;/p&gt;

&lt;p&gt;If you're still mostly serving JPEG, there's likely a &lt;strong&gt;huge optimization opportunity&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Don’t assume mobile is optimized
&lt;/h3&gt;

&lt;p&gt;Mobile networks amplify performance problems.&lt;/p&gt;

&lt;p&gt;Check your &lt;strong&gt;75th and 90th percentile metrics&lt;/strong&gt;, not just averages.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try this: Analyze a website’s image performance
&lt;/h2&gt;

&lt;p&gt;You can test any website using &lt;strong&gt;Cloudinary’s Web Speed Test&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Visit &lt;a href="https://webspeedtest.cloudinary.com" rel="noopener noreferrer"&gt;https://webspeedtest.cloudinary.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then analyze:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance score&lt;/li&gt;
&lt;li&gt;Total image weight&lt;/li&gt;
&lt;li&gt;Largest Contentful Paint&lt;/li&gt;
&lt;li&gt;Potential size reductions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You'll often discover &lt;strong&gt;massive performance gains from simple fixes&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Images are often the &lt;strong&gt;largest performance lever on a webpage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you remember only three things:&lt;/p&gt;

&lt;p&gt;1️⃣ Optimize your &lt;strong&gt;largest image first&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
2️⃣ Use &lt;strong&gt;modern formats like WebP and AVIF&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
3️⃣ Deliver &lt;strong&gt;responsive images with proper sizing&lt;/strong&gt; and explicit dimensions when you can (helps &lt;strong&gt;CLS&lt;/strong&gt;)&lt;/p&gt;

&lt;p&gt;Small improvements in image delivery can produce &lt;strong&gt;huge gains in performance and user experience&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What’s the biggest image optimization issue you’ve found on a website?&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://link.cloudinary.com/umq4j" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>webdev</category>
      <category>performance</category>
      <category>frontend</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>Cloudinary Creators Community: Learn Media APIs, Get Mentorship, and Ship Projects</title>
      <dc:creator>eugene musebe</dc:creator>
      <pubDate>Mon, 23 Feb 2026 14:13:47 +0000</pubDate>
      <link>https://dev.to/cloudinary/from-cloud-to-crowd-introducing-the-cloudinary-creators-community-3g8e</link>
      <guid>https://dev.to/cloudinary/from-cloud-to-crowd-introducing-the-cloudinary-creators-community-3g8e</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;At a glance:&lt;/strong&gt; The &lt;a href="https://cloudinary.com/developers/community?utm_source=dev-dot-to&amp;amp;utm_content=community-post" rel="noopener noreferrer"&gt;Cloudinary Creators Community&lt;/a&gt; (CCC) is a program for early-career developers and career switchers to learn image and video APIs, complete a structured course, and join a cohort with mentorship, office hours, and events—on the path to a recognized Cloudinary Creator.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What the CCC is
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Course + exam → waitlist or cohort:&lt;/strong&gt; Take the &lt;a href="https://training.cloudinary.com/courses/devrel-c2c-next?utm_source=dev-dot-to&amp;amp;utm_content=community-post" rel="noopener noreferrer"&gt;Cloud to Crowd&lt;/a&gt; course on Cloudinary Academy, pass the exam, and apply to the priority waitlist for the next cohort.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ambassador Plan, Holopin badges, and swag:&lt;/strong&gt; As a recognized Creator, you get a one-year renewable Ambassador Plan, digital badges (including Holopin) for milestones, and the CCC swag pack after your first meaningful engagement in Discord.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;12 points a year:&lt;/strong&gt; We use a light engagement model—about 12 points per year from projects, hackathons, events, and helping peers—so status stays active. (See &lt;strong&gt;The engagement path&lt;/strong&gt; below.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Vision: ‘From Cloud to Crowd’
&lt;/h2&gt;

&lt;p&gt;Today we’re opening the &lt;a href="https://cloudinary.com/developers/community?utm_source=dev-dot-to&amp;amp;utm_content=community-post" rel="noopener noreferrer"&gt;Cloudinary Creators Community&lt;/a&gt; (CCC): a structured path from the &lt;strong&gt;Cloud to Crowd&lt;/strong&gt; curriculum to cohort membership, Discord, and benefits for developers working with web media.&lt;/p&gt;

&lt;p&gt;The wait is finally over! We’re honored and excited to pull back the curtain on a project that has been fueling our passion for months.&lt;/p&gt;

&lt;p&gt;At Cloudinary, we know that the leap from finishing a coding tutorial to landing your dream role can feel like a massive chasm. That’s why we created the CCC. We aren’t just building a community; we’re building a bridge to help you cross that gap and step into your future as a leader in the tech industry.&lt;/p&gt;

&lt;p&gt;Our mission is captured in four simple words: &lt;strong&gt;from Cloud to Crowd&lt;/strong&gt;, a phrase coined by one of our community members, Jerome Hardaway.&lt;/p&gt;

&lt;p&gt;This program is specifically designed for the dreamers and the doers, the early-career developers, the brave career-changers, and the media-savvy creators who are ready to shape the future of the visual web. Whether you’re just starting to understand how images and videos power the internet or you’re already building complex apps, we’re here to give you the tools, mentorship, and global network you need to grow from a learner into a &lt;strong&gt;recognized Cloudinary Creator.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At the heart of everything we do are our three core values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Learn.&lt;/strong&gt; Master the fundamentals of modern media management and high-performance web development through structured, accessible education.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build.&lt;/strong&gt; Turn that knowledge into action! We provide the platform for you to ship real-world projects that make your portfolio shine.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connect.&lt;/strong&gt; Join a global group of peers and industry experts, with &lt;strong&gt;monthly office hours&lt;/strong&gt; and events so support is concrete, not just a catchphrase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We believe that when you combine the right tools with a supportive community, you can build work you’re proud to put in front of an employer. Ready to see how we’re making it happen?&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 1: Education
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Joining the Community: Two Pathways
&lt;/h3&gt;

&lt;p&gt;We want to make sure the CCC is accessible while maintaining the high-quality mentorship that makes this program special. There are two ways to join us:&lt;/p&gt;

&lt;h3&gt;
  
  
  The Partner Pathway
&lt;/h3&gt;

&lt;p&gt;We’re incredibly proud to launch in collaboration with global nonprofits that are changing the face of tech. If you’re a member of &lt;a href="https://gssoc.girlscript.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;GirlScript&lt;/strong&gt;&lt;/a&gt;, &lt;strong&gt;&lt;a href="https://developersinvogue.org/" rel="noopener noreferrer"&gt;Developers in Vogue&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;a href="https://vetswhocode.io/" rel="noopener noreferrer"&gt;Vets Who Code&lt;/a&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;a href="https://www.hackyourfuture.dk/" rel="noopener noreferrer"&gt;Hack Your Future&lt;/a&gt;&lt;/strong&gt;, or &lt;a href="https://www.tampadevs.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Tampa Devs&lt;/strong&gt;&lt;/a&gt;, you have a direct seat at the table! We work closely with these organizations to provide dedicated spots for their communities.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Individual Pathway
&lt;/h3&gt;

&lt;p&gt;Are you an independent builder ready to level up? You can join, too! Simply dive into our “&lt;a href="https://training.cloudinary.com/courses/devrel-c2c-next?utm_source=dev-dot-to&amp;amp;utm_content=community-post" rel="noopener noreferrer"&gt;&lt;strong&gt;Cloud to Crowd&lt;/strong&gt;&lt;/a&gt;” course at the Cloudinary Academy. Once you’ve mastered the material and passed the exam, you can submit an application to join our priority waitlist for the next available cohort.&lt;/p&gt;

&lt;p&gt;Because we keep our cohorts selective to ensure everyone gets the support they need, these spots are highly coveted, so bring your A-game.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cohorts, waitlists, and seats
&lt;/h3&gt;

&lt;p&gt;We limit active seats so mentorship stays high quality. We operate on a bi-annual model with two intake cycles. If a cohort is full, you’ll join the priority waitlist, and we invite people as seats open. Even if you’re not yet onboarded as an official Cloudinary Creator, you can still join us on Discord to take part in hackathons and challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 2: Activation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Discord: Our Digital Headquarters
&lt;/h3&gt;

&lt;p&gt;Discord is where Phase 2 happens. Once inside, you get access to private channels, technical support, and our Developer Relations staff. To keep the community vibrant, we have a 30-day rule: &lt;strong&gt;new members must introduce themselves within their first month to keep their seat.&lt;/strong&gt; You have 14 days to accept your invitation. We also monitor activity. After 60 days of silence, your status becomes inactive, and after 90 days, your spot is released to a new learner on the waitlist. This ensures our headquarters is full of creators ready to ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  Membership Benefits and Rewards
&lt;/h2&gt;

&lt;p&gt;Your growth is rewarded at every stage. As a recognized creator, you gain access to a toolkit designed to power your professional portfolio.&lt;/p&gt;

&lt;p&gt;The journey includes a one-year renewable &lt;strong&gt;Ambassador Plan&lt;/strong&gt; to ensure your personal projects perform at their best. We also celebrate your milestones with exclusive &lt;strong&gt;digital badges&lt;/strong&gt; via Holopin, giving you a way to showcase your education and project achievements to your network.&lt;/p&gt;

&lt;p&gt;Beyond the tech, you’ll join a network built on mentorship: &lt;strong&gt;office hours&lt;/strong&gt; for technical Q&amp;amp;A, tailored career advice, and opportunities within the broader Cloudinary partner network. And to welcome you properly, once you’ve made your first meaningful engagement on Discord, we’ll ship the official CCC &lt;strong&gt;Swag Pack&lt;/strong&gt; directly to your door.&lt;/p&gt;

&lt;h2&gt;
  
  
  The engagement path (12 points/year)
&lt;/h2&gt;

&lt;p&gt;We value builders over observers. To keep your creator status active, we use a simple point system with &lt;strong&gt;a goal of 12 points per year&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You earn &lt;strong&gt;5 points&lt;/strong&gt; for shipping a project and &lt;strong&gt;3&lt;/strong&gt; for joining a mini-community hackathon. Attending events earns you &lt;strong&gt;2 points&lt;/strong&gt;, while smaller acts like helping a peer solve a bug or joining a weekly standup earn &lt;strong&gt;1 point&lt;/strong&gt;. This path helps our creators stay sharp, connected, and moving forward.&lt;/p&gt;

&lt;h2&gt;
  
  
  Community Life and Activities
&lt;/h2&gt;

&lt;p&gt;The activation phase is where the energy truly shifts. We keep the momentum high with hackathons and mini challenges designed to test your skills and reward your creativity with exciting prizes.&lt;/p&gt;

&lt;p&gt;Our regular Demo Days give you a global stage to showcase your projects to the community and beyond. To track your progress, we use gamification through leaderboards and badges to celebrate our most active contributors. When you need help or career advice, our &lt;strong&gt;live office hours&lt;/strong&gt; give you direct access to people who can help you solve technical challenges and plan your next big move.&lt;/p&gt;

&lt;h2&gt;
  
  
  Professionalism and Governance
&lt;/h2&gt;

&lt;p&gt;A community this vibrant thrives on respect. Our Code of Conduct ensures that every member feels included and supported as they grow. We prioritize kindness and helpfulness, creating a professional network where everyone can advance their career safely.&lt;/p&gt;

&lt;p&gt;This is a long-term partnership. To renew your spot each year, we look for continued growth through new project submissions and engagement with your peers. Staying connected means dropping into Discord at least once every 60 days to share your progress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start on the &lt;a href="https://cloudinary.com/developers/community?utm_source=dev-dot-to&amp;amp;utm_content=community-post" rel="noopener noreferrer"&gt;Community page&lt;/a&gt;&lt;/strong&gt; to read paths, apply, and go from learner to creator—on your timeline.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://cloudinary.com/users/register_free?utm_campaign=5266-&amp;amp;utm_medium=employee_referral&amp;amp;utm_source=dev-dot-to&amp;amp;utm_content=cloud-to-crowd" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>community</category>
      <category>webdev</category>
      <category>ai</category>
      <category>beginners</category>
    </item>
    <item>
      <title>DRY January: Demotivational Posters with the TanStack or Next.js</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Mon, 26 Jan 2026 18:18:58 +0000</pubDate>
      <link>https://dev.to/cloudinary/dry-january-demotivational-posters-with-the-tanstack-or-nextjs-5164</link>
      <guid>https://dev.to/cloudinary/dry-january-demotivational-posters-with-the-tanstack-or-nextjs-5164</guid>
      <description>&lt;p&gt;January can be a time when we make all the resolutions to do and be better in the shiny new year. We resolve to do more... more exercise, a more rigorous diet, even more mindfulness, doggone it. But what if we (and hear me out) instead thought about doing LESS. Less struggling against those 4PM sugar cravings, less fighting to not take an after-lunch nap, less battling frozen driveways to back the car out so you can make it to pilates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn9qsh0vde33s9gh072fx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn9qsh0vde33s9gh072fx.png" alt="I don't know if this is creepy or inspiring, but let's build it anyway" width="800" height="693"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I don't know if this is creepy or inspiring, but let's build it anyway&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What if we embraced our inner zen and simply...did the best we could? After all, isn’t all the productivity that AI is bestowing upon us freeing up our time to do less, rather than more? I’m only half-kidding here.&lt;/p&gt;

&lt;p&gt;To encourage us to embrace a reductionist mentality, I did a little experiment to see if I could rethink some popular messaging that I’m sure you encountered if you ever worked in an American corporate office in the early 2000s: motivational posters. &lt;/p&gt;

&lt;p&gt;These little gems were plastered on the walls to exhort us to Bring Our Best Selves To Work, Achieve More, and Be Bold. Here’s an example from the &lt;a href="https://www.successories.com/" rel="noopener noreferrer"&gt;“Successories” company&lt;/a&gt;: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65gl0x461ov0amz2fvpm.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F65gl0x461ov0amz2fvpm.webp" alt="Leadership Wolfs FTW" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, they have a particular format of a black background, an inspiring photo, an uplifting word with a lot of kerning, and a blurb at the bottom to drive the message home. A &lt;strong&gt;Whole Vibe&lt;/strong&gt; was created as you walked down a poster-adorned corridor in order to toss your bag lunch into the office fridge.&lt;/p&gt;

&lt;p&gt;I particularly like this wolf. This is me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gsnex7pigdpp8c2tgds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gsnex7pigdpp8c2tgds.png" alt="skeptical wolf" width="578" height="614"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even back in those days, people made fun of these posters, and I remember being employed in one company packed with sarcastic Eastern Europeans that was instead decorated with &lt;strong&gt;&lt;em&gt;demotivational&lt;/em&gt;&lt;/strong&gt; posters such as those crafted by the brilliant &lt;a href="https://despair.com/" rel="noopener noreferrer"&gt;Despair.inc&lt;/a&gt;. Here’s a good one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu3htoxtakrayhn6zsyn0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu3htoxtakrayhn6zsyn0.png" alt="lewwwwwsers" width="790" height="638"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem with all of these posters is that, well, you have to buy them, and that’s no fun. So let’s see if we can build something like this ourselves, and actually make it a more surprising experience by connecting two APIs to generate shady messaging like this at random. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F74uean0wyf2ne26v842q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F74uean0wyf2ne26v842q.png" alt="You Got This fr fr" width="800" height="700"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;not sure what 'this' is, but you got it!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Temporarily shelve that resolution against unproductive and negative thinking and let’s see what we can build using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Affirmations API &lt;/li&gt;
&lt;li&gt;Unsplash API&lt;/li&gt;
&lt;li&gt;TanStack for a web frontend&lt;/li&gt;
&lt;li&gt;Cloudinary image storage to display the image so you can print off a PDF of your poster&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;You'll need two API keys, one for the ever-useful affirmations.dev API and one for Unsplash images. You'll also need a Cloudinary account, so &lt;a href="https://cloudinary.com?utm_campaign=5266-&amp;amp;utm_medium=employee_referral&amp;amp;utm_source=dev-dot-to&amp;amp;utm_content=dry-january" rel="noopener noreferrer"&gt;create one for free here&lt;/a&gt; if you aren't already setup. You then need to gather your Cloudinary API key, secret, cloud name and build an &lt;strong&gt;upload preset&lt;/strong&gt; so that you can upload images neatly into Cloudinary. Learn how to get credentials &lt;a href="https://cloudinary.com/documentation/developer_onboarding_faq_find_credentials?utm_campaign=5266-&amp;amp;utm_medium=employee_referral&amp;amp;utm_source=dev-dot-to&amp;amp;utm_content=dry-january#banner" rel="noopener noreferrer"&gt;here&lt;/a&gt; and how to create an upload preset &lt;a href="https://cloudinary.com/documentation/upload_presets?utm_campaign=5266-&amp;amp;utm_medium=employee_referral&amp;amp;utm_source=dev-dot-to&amp;amp;utm_content=dry-january#creating_and_managing_upload_presets" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We’re going to grab one of the pithy affirmations from the API, extract the longest word as the central message to display with plenty of kerning, and then send that word to Unsplash to find a match. Store the image returned in Cloudinary and display the lot a as a printable poster, as a stacked image, word, and affirmation on a black background, like this one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F88v2nxoyha0hgf9uywip.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F88v2nxoyha0hgf9uywip.png" alt=" " width="800" height="723"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The miracle of survival&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Continuing my tradition of building useless things to try new stacks, let’s give &lt;a href="https://tanstack.com/" rel="noopener noreferrer"&gt;TANStack&lt;/a&gt; a whirl. TanStack is a React framework that's pitched as a less 'magical' alternative to Next.js. I also built this same app using Next.js, to compare the two codebases, for learning purposes. Check out the GitHub repo with the two apps &lt;a href="https://github.com/jlooper/affirmation-posters" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclosure; I used Cursor to help build the two comparable apps in the demo repo. According to Cursor, the difference between the two boils down to your personal dev preference: "Next.js is a full-stack framework with conventions and a large ecosystem. TanStack Router/Start is a type-safe, flexible routing solution that can be extended to full-stack. The choice often comes down to preferring conventions (Next.js) vs. flexibility and type safety (TanStack)."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some differences between these two frameworks in the context of this app include:&lt;/p&gt;

&lt;h3&gt;
  
  
  Server-side data fetching architecture
&lt;/h3&gt;

&lt;p&gt;Next.js:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses Server Actions ('use server') and async Server Components&lt;/li&gt;
&lt;li&gt;Server actions in actions.ts are callable from both server and client
TanStack:&lt;/li&gt;
&lt;li&gt;Uses route loaders and createServerFn for server functions&lt;/li&gt;
&lt;li&gt;Data fetching happens in the route's loader function&lt;/li&gt;
&lt;li&gt;Loaders run on the server before the component renders&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Environment variable access
&lt;/h3&gt;

&lt;p&gt;Next.js:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses process.env.* for environment variables&lt;/li&gt;
&lt;li&gt;Variables are available on the server by default
TanStack:&lt;/li&gt;
&lt;li&gt;Uses import.meta.env.VITE_* (Vite convention)&lt;/li&gt;
&lt;li&gt;Only variables prefixed with VITE_ are exposed to the client
buildCloudinaryUrl.ts, because this app was built with Vite as its build tool.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Difference in image handling
&lt;/h3&gt;

&lt;p&gt;Next.js:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Images are displayed using the built-in optimized  component 
TanStack:&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TanStack doesn't have such a component built-in, so you need to make sure to optimize your images! This is consistent with its 'magic-free' approach.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Guess what! You can easily optimize your images in Cloudinary. While there are &lt;a href="https://cloudinary.com/blog/how-to-build-a-tanstack-start-project-for-image-optimization-and-uploading?utm_campaign=5266-&amp;amp;utm_medium=employee_referral&amp;amp;utm_source=dev-dot-to&amp;amp;utm_content=dry-january" rel="noopener noreferrer"&gt;other ways to do this&lt;/a&gt;, I decided to just add some URL transformations to the Cloudinary URL returned once the Unsplash image is uploaded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export function buildCloudinaryImageUrl(publicId: string): string {
  const cloudName = import.meta.env.VITE_CLOUDINARY_CLOUD_NAME

  if (!cloudName) {
    throw new Error('Cloudinary cloud name not configured')
  }

  const plainPublicId = publicId

  const transformations = [
    `c_fill,w_1200,h_800`, // Fill to 1200x800 for poster feel
    `f_auto`, // Auto format
    `q_auto`, // Auto quality
  ].join('/')

  // Return the complete Cloudinary URL with transformations `https://res.cloudinary.com/${cloudName}/image/upload/${transformations}/${plainPublicId}`
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you're able to grab an affirmation from the API and use it to query Unsplash, storing the result into Cloudinary and using it to build your poster. In TANStack it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const fetchNewAffirmationData = createServerFn({
  method: 'GET',
}).handler(async (): Promise&amp;lt;AffirmationData&amp;gt; =&amp;gt; {
  // First fetch the affirmation
  const affirmationData = await getAffirmation()

  // Extract the longest word from the affirmation to use as image search query
  const words = affirmationData.affirmation.split(' ').filter(word =&amp;gt; word.length &amp;gt; 0)
  const longestWord = words.reduce((longest, current) =&amp;gt; 
    current.length &amp;gt; longest.length ? current : longest, ''
  ).toLowerCase()

  // Fetch an image based on the longest word
  const accessKey = import.meta.env.VITE_UNSPLASH_ACCESS_KEY
  if (!accessKey) {
    throw new Error('Unsplash access key not configured')
  }

  const unsplashResponse = await fetch(
    `https://api.unsplash.com/photos/random?client_id=${accessKey}&amp;amp;orientation=landscape&amp;amp;query=${encodeURIComponent(longestWord)}`
  )
  if (!unsplashResponse.ok) {
    throw new Error('Failed to fetch Unsplash photo')
  }
  const unsplashData = await unsplashResponse.json() as { id: string; urls: { full: string } }

  // Upload the Unsplash image to Cloudinary
  const publicId = await uploadToCloudinary(unsplashData.urls.full)

  // Build the Cloudinary URL with transformations
  const cloudinaryUrl = buildCloudinaryImageUrl(publicId)

  return {
    affirmation: affirmationData.affirmation,
    longestWord,
    cloudinaryUrl,
    photoId: unsplashData.id,
    publicId,
  }
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And once you have all your pieces in place, you'll be able to view that matching affirmation, image, and printable styling all in one place so you can build a PDF poster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const handlePrint = () =&amp;gt; {
    if (posterRef.current) {
      const printWindow = window.open('', '_blank')
      if (printWindow) {
        printWindow.document.write(`
          &amp;lt;!DOCTYPE html&amp;gt;
          &amp;lt;html&amp;gt;
            &amp;lt;head&amp;gt;
              &amp;lt;title&amp;gt;Affirmation Poster&amp;lt;/title&amp;gt;
              &amp;lt;style&amp;gt;
                ...some fancy styling
              &amp;lt;/style&amp;gt;
            &amp;lt;/head&amp;gt;
            &amp;lt;body&amp;gt;
              &amp;lt;div class="poster-container"&amp;gt;
                &amp;lt;img src="${imageUrl}" alt="${affirmation}" class="poster-image" /&amp;gt;
                &amp;lt;h1&amp;gt;${longestWordWithDots}&amp;lt;/h1&amp;gt;
                &amp;lt;div class="poster-line"&amp;gt;&amp;lt;/div&amp;gt;
                &amp;lt;h2&amp;gt;${affirmation}&amp;lt;/h2&amp;gt;
              &amp;lt;/div&amp;gt;
            &amp;lt;/body&amp;gt;
          &amp;lt;/html&amp;gt;
        `)
        printWindow.document.close()

        // Wait for image to load before printing
        setTimeout(() =&amp;gt; {
          printWindow.focus()
          printWindow.print()
          printWindow.onafterprint = () =&amp;gt; printWindow.close()
        }, 250)
      }
    }
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So there you have it...you've built a delightfully useless little app that combines a few API calls with optimized image presentation to make you feel a little less seasonally-affected, we hope. &lt;/p&gt;

&lt;p&gt;Happy New Year! If you liked this little app, there are plenty more where it comes from from your friends at Cloudinary Developer Relations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxcjllxeu58jo1thwi6z5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxcjllxeu58jo1thwi6z5.png" alt="In boca leone" width="800" height="687"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://cloudinary.com/users/register_free?utm_campaign=5266-&amp;amp;utm_medium=employee_referral&amp;amp;utm_source=dev-dot-to&amp;amp;utm_content=dry-january" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>fullstack</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Hacktoberfest as a First-Time Maintainer</title>
      <dc:creator>Eric Portis</dc:creator>
      <pubDate>Fri, 19 Dec 2025 20:02:29 +0000</pubDate>
      <link>https://dev.to/cloudinary/hacktoberfest-as-a-first-time-maintainer-2pkn</link>
      <guid>https://dev.to/cloudinary/hacktoberfest-as-a-first-time-maintainer-2pkn</guid>
      <description>&lt;p&gt;Earlier this year, I took over maintenance duties on &lt;a href="https://github.com/cloudinary-community" rel="noopener noreferrer"&gt;Cloudinary's Community Libraries&lt;/a&gt;. Fast forward a few months, and my team was gearing up to participate in Digital Ocean's annual &lt;a href="https://hacktoberfest.com" rel="noopener noreferrer"&gt;Hacktoberfest&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Reader, I was worried.&lt;/p&gt;

&lt;p&gt;I was still very much familiarizing myself with how the libraries worked, and trying to wrap my head around their issue backlogs. And while I'd never participated in a Hacktoberfest in any capacity before, I had read some &lt;a href="https://joel.net/how-one-guy-ruined-hacktoberfest2020-drama" rel="noopener noreferrer"&gt;scary&lt;/a&gt; &lt;a href="https://domenic.me/hacktoberfest/" rel="noopener noreferrer"&gt;things&lt;/a&gt; from other maintainers, who complained of an annual avalanche of spam.&lt;/p&gt;

&lt;p&gt;What’s worse than an avalanche? A &lt;em&gt;turbo&lt;/em&gt;-avalanche powered by the recent explosion of AI-powered coding tools. As September drew to a close, I imagined hundreds of AI-written PRs coming in, each hundreds of lines long, all based on prompts that poorly understood the underlying issues, containing code that the submitters themselves didn't understand, but which we would be expected to review.&lt;/p&gt;

&lt;p&gt;The good news: It wasn't that bad. While the underlying incentive structures of Hacktoberfest &lt;em&gt;did&lt;/em&gt; generate some spam, and while almost all of that spam smelled like AI, our team was able to get ahead of many problems with strong policies, and would have been able to prevent many more with a bit of additional preparation and planning. &lt;/p&gt;

&lt;p&gt;The worst PRs were about as bad as I expected, but there weren't as many of them as I'd feared, and the best PRs were actually, &lt;em&gt;dare I say&lt;/em&gt;, really good? Hackathon participants tackled some of the issues I'd been putting off addressing for months, helping me push the libraries forward.&lt;/p&gt;

&lt;p&gt;All in all, while participation as a maintainer did take a &lt;em&gt;lot&lt;/em&gt; of time (and we're in active discussions about whether or not we want to participate next year), it also provided some tangible value, without overwhelming us. And hopefully it helped some folks get more comfortable with the mechanics of open source along the way.&lt;/p&gt;

&lt;p&gt;If you're thinking about participating as a maintainer in years to come, read on to learn about what worked for us — and what didn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Narrowing Scope
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;best&lt;/em&gt; decision we made was to limit participation to fixes for a specific set of existing issues, which we identified with a &lt;code&gt;Hacktoberfest&lt;/code&gt; tag. This addressed a few issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We had a clear mandate to quickly close any drive-by, low-value PRs (e.g. "improve docs").&lt;/li&gt;
&lt;li&gt;We could spend our time during Hacktoberfest reviewing PRs rather than trying to reproduce new issues or define new features.&lt;/li&gt;
&lt;li&gt;We could further limit contributions to issues whose fixes would be reasonably straightforward, allowing for quick reviews.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It was a good theory! In practice, we applied the &lt;code&gt;Hacktoberfest&lt;/code&gt; tag fairly liberally, and &lt;em&gt;did&lt;/em&gt; spend some time reproducing issues, specifying new features, and reviewing complex PRs. This led to a lot of work and some very long review delays.&lt;/p&gt;

&lt;p&gt;We weren't stingy enough with the tag because we set a goal for how many contributions we wanted to accept by the end of the month, &lt;em&gt;up front&lt;/em&gt;, and tagged that many issues, rather than seeing how many issues were going to be a good fit for Hacktoberfest first, and using &lt;em&gt;that&lt;/em&gt; number to set the goal.&lt;/p&gt;

&lt;p&gt;We had good reasons to set a number ahead of time. For one, it's always good to define a success metric up front so that you can quantify (and communicate) success or failure; we set the quota based on the number of successful submissions that we received in &lt;a href="https://cloudinary.com/blog/hacktoberfest-open-source-celebration-cloudinary" rel="noopener noreferrer"&gt;2024&lt;/a&gt;?utm_source=dev-dot-to&amp;amp;utm_content=hacktoberfest2025). For two, we were offering our own swag (a plushie Cloudinary unicorn) to contributors of accepted PRs, and it's best to know how many of those you're going to need well ahead of time.&lt;/p&gt;

&lt;p&gt;The end result, though, was a &lt;code&gt;Hacktoberfest&lt;/code&gt; tag on a number of issues that I only poorly or vaguely understood, which came back to bite us later.&lt;/p&gt;

&lt;p&gt;Knowing that we had a quota to hit did motivate me to do one good thing; I did a full docs review and identified and filed a handful of minor issues, which would be easy to fix and review. It would have been much (much!) faster to fix these myself, as soon as I identified them, but:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I wouldn't have done the docs review without Hacktoberfest.&lt;/li&gt;
&lt;li&gt;One of the best things Hacktoberfest does is invite people who are new to open source to learn the mechanics of it. In some ways, it doesn't matter what the contributions are, as long as new contributors are "getting their feet wet" forking, committing, PRing, and responding to feedback.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, the stage was set. As October came around, the PRs started to come in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hacktoberfest Begins
&lt;/h2&gt;

&lt;p&gt;Most of the PRs we received for the month came in during the first week. People were also very good at identifying which issues would be easiest to fix. All of my "easy" issues were taken off of the board immediately. We received a number of the predicted nonsensical drive-bys that were easy to close, but after that, it was time to chew through dozens of substantive PRs.&lt;/p&gt;

&lt;p&gt;It wasn't long before I started to regret that we'd applied the tag to some poorly defined and understood issues. The most embarrassing of these was: Someone submitted a "fix" for an issue, but their changes seemed unrelated to the problem at hand. Turns out, the issue had &lt;em&gt;already&lt;/em&gt; been fixed a year ago, as part of a much broader update, but the issue was never closed. My guess at what happened is that the submitter fed the issue into Cursor, which, being asked to solve a solved problem, did its best.&lt;/p&gt;

&lt;p&gt;It was clear that many submissions were created with the help of AI. It was also clear — both in the initial PRs that came in and in the reviews and discussions that followed — that the humans behind these submissions had varying levels of understanding of what they were trying to accomplish, and how their PRs were accomplishing it. The more understanding they had, the better things went. A number of follow-up conversations — where we tried to clarify the issue or suggest possible alternate paths forward — went nowhere, leading to closed PRs and wasted time on both sides.&lt;/p&gt;

&lt;p&gt;However, the quality of the highest-quality submissions was well beyond my expectations. A handful of contributors clearly understood the underlying frameworks better than I do, and took the time to suggest thoughtful solutions to nuanced problems that we hadn't yet been able to tackle. I'm extremely grateful for their time and effort, which was worth far more than a unicorn plushie and a Digital Ocean T-shirt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overwhelmed, but Just a Little
&lt;/h2&gt;

&lt;p&gt;Because a number of the issues turned out to be rather thorny (and because I and the other technical reviewers had more to do in October than review Hacktoberfest PRs), review times stretched to weeks, which I know was frustrating for contributors. In a couple of cases, we were not able to either accept or reject PRs before the November 15 deadline. We sent those contributors a unicorn anyway, but if the real purpose of Hacktoberfest is to familiarize folks with the mechanics of open source, learning that things are surprisingly complicated and your code might never land isn't a great lesson. Again, I wish we'd been a little more careful about which issues we invited people to solve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Worth It?
&lt;/h2&gt;

&lt;p&gt;From Cloudinary's side, it's hard to measure the value of a thing like Hacktoberfest. Our community library docs are in better shape now, and dozens of small fixes that would have been easy to put off have now been done. In addition, we made real progress on a handful of meaty issues that I'd been putting off tackling for months. And, hopefully, the participants are more familiar with Cloudinary and our SDKs, and are more likely to use us as they continue to build and grow their careers.&lt;/p&gt;

&lt;p&gt;At the same time, the slow (and in a couple of cases, unresolved) reviews may have had the opposite effect, driving participants away. And the cost in time to Cloudinary was real, as we prioritized Hacktoberfest work over other projects in October and November.&lt;/p&gt;

&lt;p&gt;Personally, I'm definitely glad I did it, once. Whether we as a company decide to do it again next year remains to be seen.&lt;/p&gt;

&lt;p&gt;Happy hacking!&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://cloudinary.com/users/register_free?utm_campaign=4870-&amp;amp;utm_medium=employee_referral&amp;amp;utm_source=dev-dot-to&amp;amp;utm_content=hacktoberfest-maintainer" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>hacktoberfest</category>
      <category>opensource</category>
      <category>community</category>
    </item>
    <item>
      <title>Build Holiday Card Collages with One Line of Code</title>
      <dc:creator>Jen Looper</dc:creator>
      <pubDate>Thu, 04 Dec 2025 14:48:59 +0000</pubDate>
      <link>https://dev.to/cloudinary/build-holiday-card-collages-with-one-line-of-code-3cp5</link>
      <guid>https://dev.to/cloudinary/build-holiday-card-collages-with-one-line-of-code-3cp5</guid>
      <description>&lt;p&gt;In this article, I'm not going to show you much code. Instead, I'm going to show you how you can build a holiday card collage, complete with a ribbon footer with text, a background, and 5 styled photos with rounded, colored borders &lt;strong&gt;in &lt;em&gt;one line&lt;/em&gt;&lt;/strong&gt; using the magic of Cloudinary's transformations. &lt;/p&gt;

&lt;p&gt;This is way cool, so buckle up and let me show you how I built this URL-generating app for your holiday card-sending needs. You can try it right now &lt;a href="https://photocardmaker.netlify.app/" rel="noopener noreferrer"&gt;here&lt;/a&gt;; it generates cards that look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsqna10jwiurvmzdb9d5f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsqna10jwiurvmzdb9d5f.png" alt=" " width="800" height="590"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Maybe this tool will even save you some money as printing photo-quality holiday cards can get expensive! &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Build an app, any app
&lt;/h2&gt;

&lt;p&gt;I have been enjoying building Astro apps, and, let's face it, using Cursor makes architecting this sort of thing a real breeze. Use your favorite AI tool to craft an app that will allow you to upload five photos to Cloudinary, rearrange them, and generate a URL to build the holiday card. &lt;/p&gt;

&lt;p&gt;💡 Here's a tip. If you are looking to quickly scaffold an app via prompting in Cursor or your IDE of choice, you can get better results by using two tools: ChatGPT to build the prompt, and Cursor to execute it.&lt;/p&gt;

&lt;p&gt;My initial lazy half-baked idea sent over to ChatGPT:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;what kind of demo could I build to create a holiday photo card maker for cloudinary&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ChatGPT, of course, thought I wanted it to build an app for me, and went straight to scaffolding a Next.js app with code everywhere. A few clarifications later, after requesting Astro for use in Cursor...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;create this as a prompt I can give to cursor&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;...ChatGPT generated a really nice, detailed prompt ready for building which you can read in full &lt;a href="https://gist.github.com/jlooper/cf5e18996ce86d00b5393de25fb0b017" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By having this kind of very detailed prompt, Cursor is able to build a perfectly reasonable app on the first try. Then it's up to the engineer to tweak it, redesign it, and add elements while keeping the core functionality of uploading and URL-building intact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add some extras and get ready to upload
&lt;/h2&gt;

&lt;p&gt;I went ahead and changed the app colors for a wintry, cozy look, adding some falling snow, a popup elf with changing holiday messages (can you find him?), glowing lights, and a candy cane border, mostly borrowed from &lt;a href="https://codepen.io/alvaromontoro/pen/xxXrzqj" rel="noopener noreferrer"&gt;CodePen&lt;/a&gt;. Just because we use AI doesn't mean we can't continue to make interesting interfaces! And AI can help you do that, too. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyemqooblxuwb5a5bajg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyemqooblxuwb5a5bajg1.png" alt="The app's top" width="800" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After those fun things are complete, you can start making sure that you're set up to use Cloudinary's image upload and image transformation capabilities. To make this work, you need to ensure that you have some environment variables set up. Create an account on &lt;a href="https://cloudinary.com?utm_source=dev-dot-to&amp;amp;utm_content=holiday-card" rel="noopener noreferrer"&gt;Cloudinary&lt;/a&gt; (there's a generous free tier) and grab your API keys from the console by clicking the "⚙️" gear icon in the left nav and selecting 'API Keys':&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fou05wjbtcfrvkec1ctae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fou05wjbtcfrvkec1ctae.png" alt="Finding your API keys in Cloudinary" width="800" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You want to add those keys to your .env file locally, and then make sure they're available on your hosting service when you push your app to the cloud. I use Netlify so I add these keys into the Netlify console before publishing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PUBLIC_CLOUDINARY_CLOUD_NAME=&amp;lt;your cloud name&amp;gt;
PUBLIC_CLOUDINARY_UPLOAD_PRESET=christmas-collage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This app also needs what's called an 'Upload Preset', which is essentially a placeholder that helps your app figure out where your uploaded images should go. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up that preset by clicking the 'Uploads' navigation link that's right under the 'API Keys' navigation on the left. This will let you set up an unsigned preset. &lt;/li&gt;
&lt;li&gt;Call it 'christmas-collage' (or something else, and make sure to update your app). &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5c1jom2vo9t8s849muat.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5c1jom2vo9t8s849muat.png" alt="Upload Preset area" width="800" height="118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make sure you have a folder set up by clicking &lt;code&gt;Assets &amp;gt; folders&lt;/code&gt; on the left nav in the Cloudinary console. Call this folder 'collages'. &lt;/li&gt;
&lt;li&gt;Also create a folder called 'holiday-assets' and add a collage background image and a background image for your ribbon element on the card. I used a white pixel image and a fancy holiday background like this:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fbeanpot-studio%2Fimage%2Fupload%2Fv1763602104%2Fholiday-assets%2Fcollage-bg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fbeanpot-studio%2Fimage%2Fupload%2Fv1763602104%2Fholiday-assets%2Fcollage-bg.jpg" alt="Background" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@jeshoots?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;JESHOOTS.COM&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/assorted-christmas-ornaments-7VOyZ0-iO0o?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Watch the URL builder work
&lt;/h2&gt;

&lt;p&gt;Now here's the interesting bit. Once you're done messing with your interface to make it look cute, take a look at the very clever way that your card is generated. First, you upload a series of 5 photos of your family to the app, and the interface lets you rearrange them into the order in the card that you want.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5qzl7l1ed0m6jt4m67c6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5qzl7l1ed0m6jt4m67c6.png" alt="uploading images for the card" width="800" height="553"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;True story, my daughter asked me what the difference is between this and PicCollage, the answer being "Mom made it!"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The URL that is generated, packed with Cloudinary image transformations, looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://res.cloudinary.com/jen-demos/image/upload/w_1600,h_900,c_fill,q_auto,f_auto
/l_holiday-assets:jhi9cpio4ahxomhagwvs/c_fill,w_473,h_680/r_25,bo_8px_solid_rgb:FFD700/fl_layer_apply,g_north_west,x_50,y_50
/l_holiday-assets:d9njoekpa1zzzepy2y5d/c_fill,w_473,h_426/r_25,bo_8px_solid_rgb:FFD700/fl_layer_apply,g_north_west,x_563,y_50
/l_holiday-assets:nm9i3sjh6c3mkfj0ari1/c_fill,w_473,h_214/r_25,bo_8px_solid_rgb:FFD700/fl_layer_apply,g_north_west,x_563,y_516
/l_holiday-assets:rg0c1ul7ux1gcgl8c2lr/c_fill,w_473,h_214/r_25,bo_8px_solid_rgb:FFD700/fl_layer_apply,g_north_west,x_1076,y_50
/l_holiday-assets:d0brmcfuqfyzwtelsiyt/c_fill,w_473,h_426/r_25,bo_8px_solid_rgb:FFD700/fl_layer_apply,g_north_west,x_1076,y_304
/l_holiday-assets:white-pixel/c_fill,w_1600,h_100/o_70/fl_layer_apply,g_south,y_20
/l_text:Pacifico_70:Our%20Family%20-%20Holiday%202025,co_rgb:000000/fl_layer_apply,g_center,y_380
/holiday-assets/collage-bg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, that one line created this card:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjen-demos%2Fimage%2Fupload%2Fw_1600%2Ch_900%2Cc_fill%2Cq_auto%2Cf_auto%2Fl_holiday-assets%3Ajhi9cpio4ahxomhagwvs%2Fc_fill%2Cw_473%2Ch_680%2Fr_25%2Cbo_8px_solid_rgb%3AFFD700%2Ffl_layer_apply%2Cg_north_west%2Cx_50%2Cy_50%2Fl_holiday-assets%3Ad9njoekpa1zzzepy2y5d%2Fc_fill%2Cw_473%2Ch_426%2Fr_25%2Cbo_8px_solid_rgb%3AFFD700%2Ffl_layer_apply%2Cg_north_west%2Cx_563%2Cy_50%2Fl_holiday-assets%3Anm9i3sjh6c3mkfj0ari1%2Fc_fill%2Cw_473%2Ch_214%2Fr_25%2Cbo_8px_solid_rgb%3AFFD700%2Ffl_layer_apply%2Cg_north_west%2Cx_563%2Cy_516%2Fl_holiday-assets%3Arg0c1ul7ux1gcgl8c2lr%2Fc_fill%2Cw_473%2Ch_214%2Fr_25%2Cbo_8px_solid_rgb%3AFFD700%2Ffl_layer_apply%2Cg_north_west%2Cx_1076%2Cy_50%2Fl_holiday-assets%3Ad0brmcfuqfyzwtelsiyt%2Fc_fill%2Cw_473%2Ch_426%2Fr_25%2Cbo_8px_solid_rgb%3AFFD700%2Ffl_layer_apply%2Cg_north_west%2Cx_1076%2Cy_304%2Fl_holiday-assets%3Awhite-pixel%2Fc_fill%2Cw_1600%2Ch_100%2Fo_70%2Ffl_layer_apply%2Cg_south%2Cy_20%2Fl_text%3APacifico_70%3AOur%2520Family%2520-%2520Holiday%25202025%2Cco_rgb%3A000000%2Ffl_layer_apply%2Cg_center%2Cy_380%2Fholiday-assets%2Fcollage-bg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fjen-demos%2Fimage%2Fupload%2Fw_1600%2Ch_900%2Cc_fill%2Cq_auto%2Cf_auto%2Fl_holiday-assets%3Ajhi9cpio4ahxomhagwvs%2Fc_fill%2Cw_473%2Ch_680%2Fr_25%2Cbo_8px_solid_rgb%3AFFD700%2Ffl_layer_apply%2Cg_north_west%2Cx_50%2Cy_50%2Fl_holiday-assets%3Ad9njoekpa1zzzepy2y5d%2Fc_fill%2Cw_473%2Ch_426%2Fr_25%2Cbo_8px_solid_rgb%3AFFD700%2Ffl_layer_apply%2Cg_north_west%2Cx_563%2Cy_50%2Fl_holiday-assets%3Anm9i3sjh6c3mkfj0ari1%2Fc_fill%2Cw_473%2Ch_214%2Fr_25%2Cbo_8px_solid_rgb%3AFFD700%2Ffl_layer_apply%2Cg_north_west%2Cx_563%2Cy_516%2Fl_holiday-assets%3Arg0c1ul7ux1gcgl8c2lr%2Fc_fill%2Cw_473%2Ch_214%2Fr_25%2Cbo_8px_solid_rgb%3AFFD700%2Ffl_layer_apply%2Cg_north_west%2Cx_1076%2Cy_50%2Fl_holiday-assets%3Ad0brmcfuqfyzwtelsiyt%2Fc_fill%2Cw_473%2Ch_426%2Fr_25%2Cbo_8px_solid_rgb%3AFFD700%2Ffl_layer_apply%2Cg_north_west%2Cx_1076%2Cy_304%2Fl_holiday-assets%3Awhite-pixel%2Fc_fill%2Cw_1600%2Ch_100%2Fo_70%2Ffl_layer_apply%2Cg_south%2Cy_20%2Fl_text%3APacifico_70%3AOur%2520Family%2520-%2520Holiday%25202025%2Cco_rgb%3A000000%2Ffl_layer_apply%2Cg_center%2Cy_380%2Fholiday-assets%2Fcollage-bg" alt="Photo Card" width="1600" height="900"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's break down that one-liner. &lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Set up the base “card” background:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.../image/upload/w_1600,h_900,c_fill,q_auto,f_auto/ ... /holiday-assets/collage-bg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;w_1600,h_900&lt;/code&gt; – make the final image 1600×900.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;c_fill&lt;/code&gt; – crop/resize to fill that aspect ratio.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;q_auto,f_auto&lt;/code&gt; – optimize quality and format automatically (WebP/AVIF/etc).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;holiday-assets/collage-bg&lt;/code&gt; – this is the base background image for the card.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Add the first photo (top left)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/l_holiday-assets:uzfzkx8d5xd0jr1jxzyo/c_fill,w_473,h_680
/r_25,bo_8px_solid_rgb:FFD700/fl_layer_apply,g_north_west,x_50,y_50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;l_holiday-assets:...&lt;/code&gt; – load this image as a layer.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;c_fill,w_473,h_680&lt;/code&gt; – size it to 473×680, cropping to fill.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;r_25&lt;/code&gt; – rounded corners (radius 25).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bo_8px_solid_rgb:FFD700&lt;/code&gt; – 8px solid gold (#FFD700) border.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fl_layer_apply,g_north_west,x_50,y_50&lt;/code&gt; – apply the layer at the top-left, offset 50px from the top and left.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Add the remaining images, some taller than others and placed in different areas of the card.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Add a semi-transparent footer bar
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/l_holiday-assets:white-pixel/c_fill,w_1600,h_100/o_70
/fl_layer_apply,g_south,y_20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This bar uses a white-pixel asset as a tiny base and stretches it to &lt;code&gt;w_1600,h_100&lt;/code&gt; to form a strip which is &lt;code&gt;o_70&lt;/code&gt; – 70% opacity (semi-transparent). It's then applied at the bottom (&lt;code&gt;g_south&lt;/code&gt;) with a small vertical offset (&lt;code&gt;y_20&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Finally, add the holiday greeting text
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/l_text:Pacifico_70:Our%20Family%20-%20Holiday%202025,co_rgb:000000
/fl_layer_apply,g_center,y_380
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;l_text:Pacifico_70:...&lt;/code&gt; – create a text layer with Font: Pacifico in size: 70. Give it the text: &lt;code&gt;Our Family - Holiday 2025&lt;/code&gt; with &lt;code&gt;co_rgb:000000&lt;/code&gt; – black text color.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fl_layer_apply,g_center,y_380&lt;/code&gt; – center the text horizontally and shift it vertically to overlay the footer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, this monster URL &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;starts with a festive background&lt;/li&gt;
&lt;li&gt;lays out five photos in a collage with rounded corners and gold frames&lt;/li&gt;
&lt;li&gt;adds a semi-transparent white bar at the bottom&lt;/li&gt;
&lt;li&gt;and writes “Our Family - Holiday 2025” in a script font on top of that bar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...rendering the whole card on demand in a single Cloudinary transformation URL. Really nice!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: you can make edits right in the URL to change elements on the card - try adding your own family name and changing the border color.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Enjoy the results
&lt;/h2&gt;

&lt;p&gt;You could save this image and print it for snail mail, or send it right away via email to the folks who are eager to hear from you this holiday season. And you did it in one line. That's awesome!&lt;/p&gt;

&lt;p&gt;Try this in &lt;a href="https://cloudinary.com?utm_source=dev-dot-to&amp;amp;utm_content=holiday-card" rel="noopener noreferrer"&gt;Cloudinary&lt;/a&gt; today, and get one more item checked off your holiday to-do list. Learn more about image transformations &lt;a href="https://cloudinary.com/documentation/image_transformations?utm_source=dev-dot-to&amp;amp;utm_content=holiday-card" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Happy holidays from Cloudinary Developer Relations to you.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Cloudinary ❤️ developers&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ready to level up your media workflow? Start using Cloudinary for free and build better visual experiences today.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👉 &lt;strong&gt;&lt;a href="https://cloudinary.com/users/register_free?utm_campaign=4870-&amp;amp;utm_medium=employee_referral&amp;amp;utm_source=dev-dot-to&amp;amp;utm_content=holiday-card" rel="noopener noreferrer"&gt;Create your free account&lt;/a&gt;&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>webdev</category>
      <category>astro</category>
      <category>react</category>
      <category>codepen</category>
    </item>
    <item>
      <title>Generative Outfits in React: Cloudinary generativeReplace, Background Replace, and Node Upload</title>
      <dc:creator>Pato</dc:creator>
      <pubDate>Thu, 06 Nov 2025 01:30:09 +0000</pubDate>
      <link>https://dev.to/cloudinary/building-a-fashion-app-using-cloudinarys-genai-in-react-and-nodejs-25k2</link>
      <guid>https://dev.to/cloudinary/building-a-fashion-app-using-cloudinarys-genai-in-react-and-nodejs-25k2</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;FashionistaAI&lt;/strong&gt; is a &lt;strong&gt;React + Vite + Node&lt;/strong&gt; sample: upload a photo, then use &lt;strong&gt;Cloudinary&lt;/strong&gt; &lt;strong&gt;GenAI&lt;/strong&gt; (&lt;code&gt;generativeReplace&lt;/code&gt;, &lt;code&gt;generativeBackgroundReplace&lt;/code&gt;, &lt;code&gt;generativeRecolor&lt;/code&gt;, &lt;code&gt;generativeRestore&lt;/code&gt;) to build &lt;strong&gt;four&lt;/strong&gt; styled looks, with &lt;strong&gt;HTTP 423&lt;/strong&gt; retry logic while derivatives finish.&lt;/p&gt;

&lt;p&gt;Upload a picture → get &lt;strong&gt;four&lt;/strong&gt; styled looks (elegant, streetwear, sporty, business casual). In this walkthrough we’ll build &lt;strong&gt;FashionistaAI&lt;/strong&gt; with &lt;strong&gt;Cloudinary GenAI&lt;/strong&gt;, &lt;strong&gt;React (Vite)&lt;/strong&gt; on the frontend, and a tiny &lt;strong&gt;Node.js/Express&lt;/strong&gt; backend for secure uploads.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Repo:&lt;/em&gt; &lt;a href="https://github.com/cloudinary-devs/Cloudinary-FashionistaAI" rel="noopener noreferrer"&gt;Cloudinary-FashionistaAI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Product note:&lt;/strong&gt; &lt;strong&gt;GenAI&lt;/strong&gt; features must be &lt;strong&gt;enabled&lt;/strong&gt; for your &lt;strong&gt;Cloudinary&lt;/strong&gt; product account and may depend on your &lt;strong&gt;plan&lt;/strong&gt;—check the console before you run the flows below.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What you’ll build
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A React app that:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;uploads an image to your Node backend
&lt;/li&gt;
&lt;li&gt;asks &lt;strong&gt;Cloudinary GenAI&lt;/strong&gt; to swap tops/bottoms (&lt;code&gt;generativeReplace&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;replaces the background (&lt;code&gt;generativeBackgroundReplace&lt;/code&gt;)
&lt;/li&gt;
&lt;li&gt;lets you &lt;strong&gt;recolor&lt;/strong&gt; top or bottom on click (&lt;code&gt;generativeRecolor&lt;/code&gt;)
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;A Node.js server that securely uploads files to Cloudinary with the official SDK.  &lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Client-side 423 handling:&lt;/strong&gt; a small &lt;strong&gt;preload&lt;/strong&gt; loop that &lt;strong&gt;retries&lt;/strong&gt; when a derived URL returns &lt;strong&gt;HTTP 423&lt;/strong&gt; (still processing).&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Demo (what it looks like)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The background adapts to the look; each tile is a different style:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Elegant
&lt;/li&gt;
&lt;li&gt;Streetwear
&lt;/li&gt;
&lt;li&gt;Sporty
&lt;/li&gt;
&lt;li&gt;Business casual&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fizvn599ke0w4kmugmf2a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fizvn599ke0w4kmugmf2a.png" alt="Fashionista app UI" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Node 18+ and npm
&lt;/li&gt;
&lt;li&gt;A free &lt;a href="https://cloudinary.com" rel="noopener noreferrer"&gt;Cloudinary&lt;/a&gt; account

&lt;ul&gt;
&lt;li&gt;GenAI features may need to be enabled depending on your plan.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Basic React/TypeScript familiarity (optional but helpful)
&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  1) Set up Cloudinary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create/Login&lt;/strong&gt; → &lt;strong&gt;Settings → Product Environments&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Confirm your &lt;strong&gt;Cloud name&lt;/strong&gt; (keep it consistent across tools).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Settings → Product Environments → API Keys&lt;/strong&gt; → &lt;strong&gt;Generate New API Key&lt;/strong&gt;.
Save: &lt;strong&gt;Cloud name&lt;/strong&gt;, &lt;strong&gt;API key&lt;/strong&gt;, &lt;strong&gt;API secret&lt;/strong&gt; (secret stays on the server).
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  2) Bootstrap the React app (Vite)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a Vite + React + TS app&lt;/span&gt;
npm create vite@latest fashionistaai &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--template&lt;/span&gt; react-ts
&lt;span class="nb"&gt;cd &lt;/span&gt;fashionistaai

&lt;span class="c"&gt;# Frontend deps&lt;/span&gt;
npm i axios @cloudinary/react @cloudinary/url-gen

&lt;span class="c"&gt;# Dev tooling&lt;/span&gt;
npm i &lt;span class="nt"&gt;-D&lt;/span&gt; @vitejs/plugin-react

&lt;span class="c"&gt;# Backend deps (we'll use one package.json for both)&lt;/span&gt;
npm i express cors cloudinary multer streamifier dotenv

&lt;span class="c"&gt;# Nice-to-have dev deps&lt;/span&gt;
npm i &lt;span class="nt"&gt;-D&lt;/span&gt; nodemon concurrently
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3) Configure Vite dev proxy (frontend → backend)
&lt;/h2&gt;

&lt;p&gt;Create/replace &lt;code&gt;vite.config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;react&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@vitejs/plugin-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;react&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
  &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:8000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;changeOrigin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This forwards any &lt;code&gt;/api/*&lt;/code&gt; calls to the Express server on port &lt;code&gt;8000&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  4) Environment variables
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;.env&lt;/code&gt; in the project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Server (Node) reads these:&lt;/span&gt;
&lt;span class="nv"&gt;CLOUDINARY_CLOUD_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR_CLOUD_NAME
&lt;span class="nv"&gt;CLOUDINARY_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY
&lt;span class="nv"&gt;CLOUDINARY_API_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_SECRET

&lt;span class="c"&gt;# Frontend (Vite) reads those prefixed with VITE_&lt;/span&gt;
&lt;span class="nv"&gt;VITE_CLOUDINARY_CLOUD_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR_CLOUD_NAME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Never&lt;/strong&gt; expose &lt;code&gt;CLOUDINARY_API_SECRET&lt;/code&gt; on the frontend. That’s why we’re using a server.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  5) Node/Express backend (&lt;code&gt;server.js&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;server.js&lt;/code&gt; in the project root. You can find the complete server file &lt;a href="https://github.com/cloudinary-devs/Cloudinary-FashionistaAI/blob/main/server.js" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Below: the main pieces of &lt;code&gt;server.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Add to &lt;code&gt;server.js&lt;/code&gt; (top of file) — wire up your Cloudinary credentials from &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;cloud_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLOUDINARY_CLOUD_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLOUDINARY_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;api_secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLOUDINARY_API_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This connects the server to your Cloudinary account.&lt;/p&gt;

&lt;p&gt;We use &lt;strong&gt;&lt;code&gt;multer&lt;/code&gt; in memory&lt;/strong&gt; so the file never hits disk. The &lt;strong&gt;Node SDK&lt;/strong&gt; accepts a &lt;strong&gt;stream&lt;/strong&gt;; &lt;strong&gt;&lt;code&gt;streamifier&lt;/code&gt;&lt;/strong&gt; turns the in-memory &lt;strong&gt;buffer&lt;/strong&gt; into a stream for &lt;code&gt;upload_stream&lt;/code&gt;, with a &lt;strong&gt;10MB&lt;/strong&gt; cap and &lt;strong&gt;PNG / JPG / WEBP&lt;/strong&gt; &lt;code&gt;fileFilter&lt;/code&gt; only.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;storage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;multer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;memoryStorage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;upload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;multer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fileSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// 10MB&lt;/span&gt;
  &lt;span class="na"&gt;fileFilter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/image&lt;/span&gt;&lt;span class="se"&gt;\/(&lt;/span&gt;&lt;span class="sr"&gt;png|jpe&lt;/span&gt;&lt;span class="se"&gt;?&lt;/span&gt;&lt;span class="sr"&gt;g|webp&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mimetype&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Only PNG/JPG/WEBP images are allowed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uploadStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uploader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upload_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;resource_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cloudinary error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;streamifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uploadStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;SDK&lt;/strong&gt; can take a path or a &lt;strong&gt;stream&lt;/strong&gt;; here &lt;strong&gt;&lt;code&gt;multer&lt;/code&gt;&lt;/strong&gt; left the bytes in &lt;strong&gt;memory&lt;/strong&gt;, we &lt;strong&gt;stream&lt;/strong&gt; to &lt;strong&gt;&lt;code&gt;upload_stream&lt;/code&gt;&lt;/strong&gt;, and the handler returns the &lt;strong&gt;JSON&lt;/strong&gt; your React app needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;package.json scripts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;package.json&lt;/code&gt; and add these scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"server"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nodemon server.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start:both"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concurrently -k &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm:server&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;npm:dev&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can run &lt;strong&gt;both&lt;/strong&gt; servers with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run start:both
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Or use two terminals: &lt;code&gt;npm run server&lt;/code&gt; and &lt;code&gt;npm run dev&lt;/code&gt;.)&lt;/p&gt;




&lt;h2&gt;
  
  
  6) React UI (&lt;code&gt;src/App.tsx&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;The UI is a &lt;strong&gt;drop-in&lt;/strong&gt;, TypeScript-friendly &lt;code&gt;App&lt;/code&gt; that keeps your original logic, tightens types, separates file vs. Cloudinary image state, and reads the cloud name from env. &lt;a href="https://github.com/cloudinary-devs/Cloudinary-FashionistaAI/blob/main/src/App.tsx" rel="noopener noreferrer"&gt;Full &lt;code&gt;App.tsx&lt;/code&gt; in the repo&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the clothing styles
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;StyleKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;top&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bottom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;StyleConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;STYLES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;StyleConfig&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;suit jacket for upper body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;suit pants for lower body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;office&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;business casual&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sport tshirt for upper body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sport shorts for lower body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gym&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sporty&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;streetwear shirt for upper body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;streetwear pants for lower body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;street&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;streetwear&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;elegant tuxedo for upper body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;elegant tuxedo pants for lower body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gala&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;elegant&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;StyleKey&lt;/code&gt; is &lt;strong&gt;top&lt;/strong&gt; vs. &lt;strong&gt;bottom&lt;/strong&gt; for &lt;strong&gt;recolor&lt;/strong&gt;; &lt;code&gt;StyleConfig&lt;/code&gt; is one full look (clothing + background + label). &lt;code&gt;STYLES&lt;/code&gt; is the “wardrobe” that becomes each card (&lt;strong&gt;Business casual&lt;/strong&gt;, &lt;strong&gt;Sporty&lt;/strong&gt;, &lt;strong&gt;Streetwear&lt;/strong&gt;, &lt;strong&gt;Elegant&lt;/strong&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Submitting to the backend and getting the base image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;setLooks&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt;
    &lt;span class="nf"&gt;setLoadingStatus&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/generate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;multipart/form-data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;publicId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_id&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cld&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;width&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;508&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;height&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;508&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="nf"&gt;setBaseImg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;createLooks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Upload failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;POST /api/generate&lt;/code&gt; uploads the file, returns a &lt;strong&gt;&lt;code&gt;public_id&lt;/code&gt;&lt;/strong&gt;, the UI builds a base &lt;code&gt;CloudinaryImage&lt;/code&gt; at 508×508, then &lt;code&gt;createLooks(publicId)&lt;/code&gt; runs the generative stack.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preloading derived images (poll until ready)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CloudinaryImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toURL&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nf"&gt;setLoadingStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;copy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="nx"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;copy&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onerror&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// 423 means "still deriving" on Cloudinary&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HEAD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;423&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempts&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
      &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error loading image. Please try again.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;setLoadingStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;copy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="nx"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;copy&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;preload&lt;/code&gt; loads a derived &lt;strong&gt;URL&lt;/strong&gt; in a hidden &lt;code&gt;Image&lt;/code&gt; &lt;strong&gt;onload&lt;/strong&gt; clears that tile’s &lt;strong&gt;spinner&lt;/strong&gt;. &lt;strong&gt;Onerror&lt;/strong&gt;, a &lt;strong&gt;HEAD&lt;/strong&gt; can return &lt;strong&gt;HTTP 423&lt;/strong&gt; while Cloudinary is still &lt;strong&gt;building&lt;/strong&gt; the &lt;strong&gt;derivative&lt;/strong&gt;, so we &lt;strong&gt;back off&lt;/strong&gt; and &lt;strong&gt;retry&lt;/strong&gt;; otherwise we surface an error. That’s the &lt;strong&gt;“423 + retry”&lt;/strong&gt; &lt;strong&gt;pattern&lt;/strong&gt; from the &lt;strong&gt;summary&lt;/strong&gt; above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the different looks (generative effects)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createLooks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;STYLES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cld&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generativeReplace&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shirt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generativeReplace&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pants&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generativeBackgroundReplace&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// optional: prompt with your background&lt;/span&gt;
      &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generativeRestore&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;width&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;height&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nf"&gt;setLooks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imgs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;setLoadingStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nx"&gt;imgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; for each &lt;code&gt;style&lt;/code&gt;, &lt;code&gt;generativeReplace().from('shirt').to(style.top)&lt;/code&gt; and &lt;code&gt;.from('pants').to(style.bottom)&lt;/code&gt; map prompts like “suit jacket for upper body” to the &lt;strong&gt;garment&lt;/strong&gt; swaps, then &lt;strong&gt;background&lt;/strong&gt; + &lt;strong&gt;restore&lt;/strong&gt; + &lt;strong&gt;resize&lt;/strong&gt; complete the card before &lt;strong&gt;preload&lt;/strong&gt; runs per tile.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recolor modal logic
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;openRecolorModal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setSelectedLookIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;setOpenModal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;applyRecolor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;looks&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;selectedLookIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="nf"&gt;setLoadingStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;copy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="nx"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;selectedLookIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;copy&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nf"&gt;setOpenModal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// Recolor only the chosen item for the chosen look&lt;/span&gt;
    &lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;effect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generativeRecolor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;STYLES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;selectedLookIndex&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;selectedItem&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;setLooks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;selectedLookIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Recolor&lt;/strong&gt; layers &lt;strong&gt;&lt;code&gt;generativeRecolor&lt;/code&gt;&lt;/strong&gt; on an already &lt;strong&gt;generated&lt;/strong&gt; tile, then &lt;strong&gt;preload&lt;/strong&gt; waits for the new &lt;strong&gt;derivative&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Add styling from the &lt;a href="https://github.com/cloudinary-devs/Cloudinary-FashionistaAI" rel="noopener noreferrer"&gt;repo CSS&lt;/a&gt; or your own.&lt;/p&gt;




&lt;h2&gt;
  
  
  7) How it works (quick tour)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Upload&lt;/strong&gt;: The file is sent to &lt;code&gt;POST /api/generate&lt;/code&gt;. The server uses &lt;code&gt;cloudinary.uploader.upload_stream&lt;/code&gt; to store it and returns the &lt;strong&gt;&lt;code&gt;public_id&lt;/code&gt;&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Transform:&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;generativeReplace().from('shirt').to(style.top)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;generativeReplace().from('pants').to(style.bottom)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;generativeBackgroundReplace()&lt;/code&gt; (optionally prompt it to steer the scene)
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;generativeRestore()&lt;/code&gt; for quality
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Recolor&lt;/strong&gt;: On a generated tile, open a modal and apply &lt;code&gt;generativeRecolor(&amp;lt;item&amp;gt;, &amp;lt;hex&amp;gt;)&lt;/code&gt;.  &lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;423 handling&lt;/strong&gt;: When the first request for a derived image hits Cloudinary while it’s still being generated, you might see HTTP &lt;strong&gt;423&lt;/strong&gt;. The preload helper retries with backoff; for heavy use, consider preparing &lt;em&gt;eager transformations&lt;/em&gt; on upload.  &lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  8) Testing locally
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install (already done if you followed along)&lt;/span&gt;
npm i

&lt;span class="c"&gt;# Run both servers&lt;/span&gt;
npm run start:both
&lt;span class="c"&gt;# Frontend: http://localhost:3000&lt;/span&gt;
&lt;span class="c"&gt;# Backend:  http://localhost:8000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Production notes (optional but recommended)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Secrets&lt;/strong&gt;: Keep &lt;code&gt;CLOUDINARY_API_SECRET&lt;/code&gt; server-side only; use environment vars on your host.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upload presets&lt;/strong&gt;: Lock down transformations and content rules with a Cloudinary upload preset.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limits&lt;/strong&gt;: Add rate limiting to your API if you open it to the public.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation&lt;/strong&gt;: Keep the Multer &lt;code&gt;fileFilter&lt;/code&gt; and &lt;code&gt;limits&lt;/code&gt; in place; consider scanning/validating uploads.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching/CDN&lt;/strong&gt;: Cloudinary URLs are CDN-backed; reusing the same &lt;code&gt;public_id&lt;/code&gt; improves cache hits.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility&lt;/strong&gt;: Provide helpful &lt;code&gt;alt&lt;/code&gt; text for generated images (the example includes captions).
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;FashionistaAI&lt;/strong&gt; shows how a small &lt;strong&gt;React&lt;/strong&gt; app plus &lt;strong&gt;Cloudinary&lt;/strong&gt; &lt;strong&gt;GenAI&lt;/strong&gt; can turn one &lt;strong&gt;upload&lt;/strong&gt; into four on-brand looks, with &lt;strong&gt;background&lt;/strong&gt; swaps and &lt;strong&gt;recolor&lt;/strong&gt;. Fork it, tweak the prompts, and ship your own &lt;strong&gt;AI&lt;/strong&gt;-powered try-on flow.&lt;/p&gt;

&lt;p&gt;If you build something with this, drop a link—&lt;strong&gt;DEV&lt;/strong&gt; readers will want to see it!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>programming</category>
      <category>react</category>
    </item>
  </channel>
</rss>
