Add Cloudinary for fast, safe product images
Connect Cloudinary, upload your product photos with signed requests, and serve resized, compressed images with proper alt text.
You will have: Product images stored on Cloudinary, delivered in optimized sizes, with alt text on every image and a locked-down signed-upload endpoint ready for the admin panel.
Before you start
- Episode 8 is merged: product pages show placeholder images
- A free Cloudinary account (sign up at cloudinary.com). Note the cloud name shown in your dashboard
- A handful of product photos on your computer (JPG, PNG or WebP), or let the agent create simple sample images for the seed products
- Be ready to paste your Cloudinary API key and API secret into the file .env.local yourself. Never paste them into the chat
Make it yours
Change the example (a fashion store) to your own. Your answers are saved in this browser and used in every episode.
Other series keywords (7)
Your prompt
Paste into: {{ai_agent}} opened in your project folder.You are a senior full-stack developer working inside my Next.js (App Router), TypeScript, Tailwind and Drizzle project for "Thread & Needle", a fashion e-commerce store selling women's and men's clothing, shoes and accessories. I am not a programmer, so explain technical words briefly.
Goal: store and deliver product images with Cloudinary, replacing the placeholder images from Episode 8. Read Cloudinary's current official documentation for exact function names and upload options before coding, and tell me which pages you used.
Plan first: show me your plan, including the Cloudinary folder layout, the database change and the image sizes, and wait for my OK before changing anything.
Keys: you need the cloud name, the API key and the API secret. Do not ask me to type them in this chat. Tell me the exact names to add to .env.local, let me paste the values there myself, and add only the names to the committed .env.example. You may check that each name is set, but never print, log or commit a value. Only the cloud name may reach browser code.
Tasks, after I approve:
1. Create a branch named feature/cloudinary.
2. Add the Cloudinary server library and a server-only module that reads the environment values and fails clearly if one is missing.
3. Database: make sure each image record stores the Cloudinary public id (not a full URL), alt text (required, not empty), width, height, sort order and which product it belongs to. Create the change as a drizzle-kit migration.
4. Delivery: build one helper that turns a public id and a width into an optimized URL using automatic format and automatic quality, and wire it into next/image or the Cloudinary-recommended approach for Next.js. Allow only my own Cloudinary cloud in the image settings, not every Cloudinary address. Use sensible sizes for listing cards, galleries and the cart, lazy loading below the fold, and priority only for the main product image.
5. Signed upload: create a server route that returns a signature for an upload, restricted to a fixed folder for Thread & Needle products, allowed formats JPG, PNG and WebP only (no SVG), and a short-lived timestamp. Until accounts arrive in Episode 11, this route must deny every request with a 403 and a code comment saying that the admin check is added in Episode 11. After upload, the save step must confirm the result belongs to my folder and has an allowed format before writing to the database, and must refuse files over 5 MB.
6. Seed images: write a one-off script that uploads my photos (or generated sample images) to Cloudinary using the server keys, saves the public ids and alt text for the seeded products, and can be re-run safely without duplicating images.
7. Replace the placeholders on the shop and product pages. Each image needs meaningful alt text that describes the product, such as the item, color and view, and decorative images get an empty alt.
Rules:
- Secrets stay in .env.local only. If one ever appears in a file, log or git history, stop and tell me so I can rotate it.
- Never accept an upload URL or file path from a visitor, and never expose upload or delete to the public.
Verify by running yourself and showing results: type check, lint, production build, a test that the signing route returns 403 today, a test of the URL helper, and a check that product pages load images from my cloud with f_auto and q_auto.
Commit in small steps, push and open a pull request.
Report back in this format:
1. What you did.
2. What I should check myself.
3. Anything that needs my decision.Review the prompt, then copy it. Never paste real passwords or API keys into a chat.
How to check it worked
- In your Cloudinary dashboard, open the media library: you see a folder for your store with the product images inside
- Open a product page: the real photo shows, and right-click then Open image in new tab shows a Cloudinary address that includes your cloud name
- Open the browser's developer tools, Network tab, reload a listing page: image files are small (listing cards far smaller than the original photo)
- Every image in the database has alt text; the agent's report lists any that it had to write and asks you to review them
- Visit the upload signing address in your browser: you get a denied response, not a signature
- Search your project for your API secret (the agent can run this check without showing it): it appears only in .env.local
Sample result
Fashion store exampleExample agent report for the fashion store, plus how one stored image id becomes three delivery sizes. Values shown are placeholders.
If something goes wrong
- Images do not load and the page shows a next/image error about the host.
- The image settings must allow your Cloudinary address. Ask: "Show me the image host rule in the Next.js config and make sure it matches my cloud name exactly." Restart the dev server after the change.
- The seed upload script fails with an authentication error.
- Re-check the three values in .env.local with no spaces or quotes around them, copy them again from the Cloudinary dashboard, and rerun. Do not paste the values into the chat.
- You pasted a secret into the chat or committed it by mistake.
- Treat it as leaked: create a new API secret in the Cloudinary dashboard, update .env.local, and ask the agent to check git history. Removing the file later does not make an old secret safe.
- Photos look blurry or are far too large to load quickly.
- Upload photos at least 1600 px on the long side, and ask the agent to confirm each place uses a width close to its display size.
Cloudinary has a free plan with monthly usage credits for storage and bandwidth. The limits change, so verify them on Cloudinary's pricing page before you rely on them.
More in Storefront
- Episode 8Build the catalogue: categories, search and product pages
- Episode 10Build a shopping cart that trusts the server, not the browser
- Episode 11Add secure customer accounts with sign up and login
- Episode 12Build checkout and save a pending order
This is part of the series Build a secure, no-cost e-commerce website with AI agents. See all 24 episodes, the tools used and answers to common questions on the series page.