Skip to content

Instantly share code, notes, and snippets.

@jlooper
Created December 3, 2025 18:15
Show Gist options
  • Select an option

  • Save jlooper/cf5e18996ce86d00b5393de25fb0b017 to your computer and use it in GitHub Desktop.

Select an option

Save jlooper/cf5e18996ce86d00b5393de25fb0b017 to your computer and use it in GitHub Desktop.
Full Cursor Prompt for Family Cardmaker
Prompt for Cursor: Build a “Family Holiday Collage Maker” with Cloudinary (Astro + React + Tailwind)
You are an expert full-stack engineer.
Create a small, clean Astro app called “Family Holiday Collage Maker” that uses Cloudinary to let a family upload photos and generate a single shareable holiday collage image via Cloudinary transformations.
Tech + Setup Requirements
Use Astro as the framework.
Use React components inside Astro for interactive parts.
Use TypeScript where appropriate.
Use Tailwind CSS for styling.
The app should run with npm install and npm run dev without extra steps.
Use environment variables:
PUBLIC_CLOUDINARY_CLOUD_NAME
PUBLIC_CLOUDINARY_UPLOAD_PRESET
Ensure the Cloudinary Upload Widget script is added to the main layout/head:
<script src="https://widget.cloudinary.com/v2.0/global/all.js"></script>
Core User Story
Goal:
A family visits the site, uploads photos, and the app builds a single collage image URL using Cloudinary transformations. The collage:
Uses a festive background image (hosted in Cloudinary).
Overlays up to 3 uploaded images in a simple grid.
Adds a text overlay like: The Looper Family – Holiday 2025.
Shows the final collage preview and a copyable URL.
Pages and Components
1. Main page: /
Use an Astro page (e.g. src/pages/index.astro) that:
Imports and renders a React component handling the interactive behavior (e.g. FamilyCollageApp.tsx).
Provides a clean layout shell (centered container, basic typography).
2. React Component: FamilyCollageApp
Create a React component (e.g. src/components/FamilyCollageApp.tsx) that handles:
State:
type Photo = { publicId: string; url: string };
const [familyName, setFamilyName] = useState("Looper Family");
const [photos, setPhotos] = useState<Photo[]>([]);
Layout and behavior:
Header
Title: “Family Holiday Collage Maker”
Short subtitle:
“Invite your family to upload photos, then generate a shareable holiday collage powered by Cloudinary.”
Controls
Text input for Family name (default "Looper Family").
“Add family photos” button that opens the Cloudinary Upload Widget.
Uploads Gallery
When photos.length > 0, show a grid of thumbnails (3 columns on desktop, responsive on mobile).
Each image uses object-cover, rounded corners.
Generated Collage
If no photos: show subtle text “Upload at least one photo to see your collage.”
If there are photos:
Build a collage URL using a helper function (buildCollageUrl).
Display the resulting <img> with the collage.
Show:
A read-only text box with the URL.
A “Copy URL” button that copies to clipboard and briefly shows “Copied!” feedback.
Use Tailwind for styling (e.g. max-w-4xl mx-auto p-6 space-y-8, etc.).
Cloudinary Upload Widget Component
Create a reusable React component, e.g. src/components/UploadWidget.tsx:
Uses window.cloudinary.createUploadWidget.
Config options:
cloudName: import.meta.env.PUBLIC_CLOUDINARY_CLOUD_NAME
uploadPreset: import.meta.env.PUBLIC_CLOUDINARY_UPLOAD_PRESET
sources: ["local", "camera", "url"]
multiple: true
maxFiles: 10
Accepts an onUpload(info) callback prop that receives info.public_id and info.secure_url.
Type definition:
type UploadWidgetProps = {
onUpload: (info: { public_id: string; secure_url: string }) => void;
};
Behavior:
Use useRef to store the widget instance and useEffect to initialize it once on the client.
When the widget callback fires with result.event === "success", call onUpload(result.info).
Expose a button that calls widgetRef.current.open().
Collage URL Logic (Important)
Implement a pure function in a separate file (e.g. src/lib/collage.ts) to build the collage URL from:
cloudName: string
photos: { publicId: string }[]
familyName: string
Assumptions:
Background image public ID: holiday-assets/collage-bg
Final collage dimensions: 1600×900
Use at most 3 photos for now.
Steps in the URL:
Base:
https://res.cloudinary.com/<cloudName>/image/upload
Transformation segments (in order):
Base size & quality:
w_1600,h_900,c_fill,q_auto,f_auto
For each of up to 3 photos, overlay with positions like:
l_<publicId>,w_450,h_450,c_fill,g_north_west,x_80,y_120
l_<publicId>,w_450,h_450,c_fill,g_north,y_120
l_<publicId>,w_450,h_450,c_fill,g_north_east,x_80,y_120
Note: If publicId contains a folder, e.g. family-uploads/photo1, convert / to : when used as an overlay: l_family-uploads:photo1.
Text overlay for family name:
l_text:Montserrat_60_bold:<URL_ENCODED_TEXT>,co_rgb:ffffff,g_south,y_80
<URL_ENCODED_TEXT> should be something like Looper%20Family%20–%20Holiday%202025.
Append the background public ID:
holiday-assets/collage-bg
So the final URL shape:
https://res.cloudinary.com/<cloudName>/image/upload/
w_1600,h_900,c_fill,q_auto,f_auto/
l_family-uploads:photo1,w_450,h_450,c_fill,g_north_west,x_80,y_120/
l_family-uploads:photo2,w_450,h_450,c_fill,g_north,y_120/
l_family-uploads:photo3,w_450,h_450,c_fill,g_north_east,x_80,y_120/
l_text:Montserrat_60_bold:Looper%20Family%20–%20Holiday%202025,co_rgb:ffffff,g_south,y_80/
holiday-assets/collage-bg
Implement this assembly in TypeScript with proper URL encoding and defensive checks (e.g., if only 1 or 2 photos are present).
Tailwind + Astro Integration
Configure Tailwind for Astro (standard setup with tailwind.config.cjs, postcss.config.cjs, and integration in astro.config.mjs).
Apply Tailwind classes in both the Astro layout and React components.
Use a main layout (e.g. src/layouts/BaseLayout.astro) that:
Includes the Tailwind-powered <body> shell.
Adds the Cloudinary widget script in the <head>.
Wraps the page content in a centered container.
File Structure (example suggestion)
You may choose a reasonable structure, but here’s a suggested outline:
src/pages/index.astro – main page, imports FamilyCollageApp.
src/components/FamilyCollageApp.tsx – main React app component.
src/components/UploadWidget.tsx – Cloudinary upload widget wrapper.
src/lib/collage.ts – buildCollageUrl helper.
src/layouts/BaseLayout.astro – layout with script + container.
Tailwind + Astro config files.
What to Deliver
All necessary Astro + React + TypeScript + Tailwind setup.
A working flow:
Upload images via Cloudinary Upload Widget.
Show gallery of uploaded photos.
Generate and display a Cloudinary collage URL + image.
Copy-to-clipboard button with simple “Copied!” feedback.
Clean, readable code with comments where helpful.
A minimal README.md explaining:
Required env vars (PUBLIC_CLOUDINARY_CLOUD_NAME, PUBLIC_CLOUDINARY_UPLOAD_PRESET).
How to run the app locally (npm install, npm run dev).
Please now generate the full implementation (all necessary files) so I can run this app immediately in a fresh Astro project.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment