Skip to content

musebe/react-travel-gallery

Repository files navigation

Wanderlust Travel Gallery

A production-ready travel photo gallery built with React, Vite, TypeScript, and the Cloudinary React SDK. Upload photos from any device, browse your entire Cloudinary library, and view full-size previews — all without writing a single line of upload or image-pipeline code.

Scaffolded from create-cloudinary-react.


What This Demonstrates

Feature How it works
Upload from anywhere Cloudinary Upload Widget — drag-and-drop, camera, URL, Google Drive, Dropbox
Gallery of existing images Cloudinary Admin API fetched through a Vite dev-server proxy (API secret never reaches the browser)
Automatic optimisation AdvancedImage + thumbnail() + format(auto()) + quality(auto())
Blur-up lazy loading placeholder({ mode: 'blur' }) + lazyload() plugins
Full-size preview Radix Dialog triggered on card click

Tech Stack

  • React 19 + TypeScript
  • Vite 6 (dev server, proxy, bundler)
  • Tailwind CSS v4 (configured entirely in CSS — no tailwind.config.js)
  • shadcn/ui — Button, Badge, Dialog, Skeleton
  • @cloudinary/reactAdvancedImage, placeholder, lazyload
  • @cloudinary/url-gen — transformation builder
  • Lucide React — icons
  • Sonner — toast notifications

Prerequisites


Environment Variables

The project needs five variables in a .env file at the project root.

# .env

# ── Client-side (VITE_ prefix = bundled into browser) ──────────────────────────
VITE_CLOUDINARY_CLOUD_NAME=your-cloud-name          # Dashboard → top-right corner
VITE_CLOUDINARY_UPLOAD_PRESET=your-unsigned-preset  # Settings → Upload → Upload presets
VITE_CLOUDINARY_FOLDER=travel                       # Optional: filter gallery to this folder

# ── Server-side only (no VITE_ prefix = never bundled) ─────────────────────────
CLOUDINARY_API_KEY=your-api-key                     # Dashboard → Settings → API Keys
CLOUDINARY_API_SECRET=your-api-secret               # Dashboard → Settings → API Keys

Security: CLOUDINARY_API_KEY and CLOUDINARY_API_SECRET must never have the VITE_ prefix. Without it, Vite never bundles them into the browser. The Vite dev-server proxy reads them in Node.js and uses them to authenticate Admin API requests — the secret stays on the server.

Finding your values

Variable Where to find it
VITE_CLOUDINARY_CLOUD_NAME Cloudinary Console → your cloud name in the top-right
VITE_CLOUDINARY_UPLOAD_PRESET Console → Settings → Upload → Upload presets → create an Unsigned preset
VITE_CLOUDINARY_FOLDER The folder your upload preset saves into (e.g. travel). Leave blank to show all images.
CLOUDINARY_API_KEY Console → Settings → API Keys
CLOUDINARY_API_SECRET Console → Settings → API Keys (click the eye icon)

Setup

# 1. Clone or scaffold
npx create-cloudinary-react react-travel-gallery --ts
cd react-travel-gallery

# 2. Install dependencies (already run by the CLI — skip if already installed)
npm install

# 3. Configure environment variables
cp .env.example .env          # or create .env manually
#    → fill in the five variables above

# 4. Start the dev server
npm run dev
# → http://localhost:5173

Always restart the dev server after editing .env — Vite reads env files at startup.


Project Structure

react-travel-gallery/
├── index.html                     ← Upload Widget script loaded here (async)
├── vite.config.ts                 ← Tailwind plugin + Admin API proxy
├── .env                           ← Your credentials (never commit this)
└── src/
    ├── cloudinary/
    │   ├── config.ts              ← Cloudinary instance + env vars
    │   └── UploadWidget.tsx       ← Upload Widget React wrapper (polling pattern)
    ├── lib/
    │   ├── utils.ts               ← cn() helper (clsx + tailwind-merge)
    │   └── cloudinary-api.ts      ← Admin API client (fetch all images via proxy)
    ├── components/ui/
    │   ├── button.tsx             ← shadcn Button (CVA variants)
    │   ├── badge.tsx              ← shadcn Badge
    │   ├── dialog.tsx             ← Radix Dialog (preview modal)
    │   └── skeleton.tsx           ← Loading placeholder
    ├── App.tsx                    ← Gallery state, image display, preview dialog
    ├── App.css                    ← Minimal (all styling via Tailwind)
    └── index.css                  ← Tailwind v4 + shadcn design tokens

How Cloudinary Features Work

1. Upload Widget

The widget is loaded as an external <script async> in index.html. Because it's async, window.cloudinary.createUploadWidget may not be attached by the time React mounts. UploadWidget.tsx handles this with a polling pattern:

// Poll every 100ms until the function is available (max 10 seconds)
poll = setInterval(() => {
  if (typeof window.cloudinary?.createUploadWidget === 'function') {
    clearInterval(poll)
    widgetRef.current = window.cloudinary.createUploadWidget(config, callback)
  }
}, 100)

2. Image Transformations

AdvancedImage renders an <img> whose src is a Cloudinary CDN URL built from a transformation chain:

cld.image(publicId)
  .resize(thumbnail().width(400).height(400))   // smart square crop
  .delivery(format(auto()))                      // WebP / AVIF per browser
  .delivery(quality(autoQuality()))              // smallest file, best quality

3. Admin API (gallery of existing images)

Fetching all images requires the Cloudinary Admin API, which uses Basic Auth (API key + secret). The secret must never leave the server. The Vite dev-server proxy handles this:

Browser  →  /api/cloudinary/v1_1/{cloud}/resources/image
Vite     →  adds Authorization: Basic base64(key:secret)
         →  forwards to api.cloudinary.com

The browser never sees the secret. See vite.config.ts and src/lib/cloudinary-api.ts.

Production note: The Vite proxy only runs during npm run dev. For production deployments you need a server-side route (Next.js API route, Express endpoint, edge function) that performs the same auth.


Available Scripts

npm run dev       # Start dev server (http://localhost:5173)
npm run build     # Type-check + production build → dist/
npm run lint      # ESLint
npm run preview   # Preview the production build locally

Further Reading

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages