diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index b5455b7..3ed9612 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -53,11 +53,13 @@ jobs:
with:
ref: main
fetch-depth: 0
+ fetch-tags: true
token: ${{ secrets.RELEASE_TOKEN }}
- name: Setup git branch
run: |
git fetch --all --tags --force
+ git fetch origin '+refs/notes/*:refs/notes/*' || true
git checkout -B main
git branch --set-upstream-to=origin/main main
@@ -85,16 +87,13 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- - name: Create initial tag if needed
- if: github.event.inputs.dry_run != 'true'
+ - name: Add semantic-release note (dry run only)
+ if: github.event.inputs.dry_run == 'true'
run: |
- if ! git rev-parse --verify "v1.0.0-beta.1" >/dev/null 2>&1; then
- echo "Creating initial tag v1.0.0-beta.1"
- git tag -a "v1.0.0-beta.1" -m "chore: initial beta release"
- git push origin "v1.0.0-beta.1" || echo "Tag push failed (may not have permission or tag exists)"
- else
- echo "Tag v1.0.0-beta.1 already exists"
- fi
+ set -e
+ if ! git rev-parse --verify v1.0.0-beta.1 >/dev/null 2>&1; then exit 0; fi
+ git notes --ref semantic-release-v1.0.0-beta.1 add -f -m '{"channels":["beta"]}' v1.0.0-beta.1
+ echo "Added semantic-release note to v1.0.0-beta.1 (local only for dry run)."
- name: Dry run mode notice
if: github.event.inputs.dry_run == 'true'
@@ -130,26 +129,34 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
run: |
+ set -e
if [ "${{ github.event.inputs.dry_run }}" = "true" ]; then
echo "Running semantic-release in DRY RUN mode..."
npx semantic-release --dry-run 2>&1 | tee semantic-release.log || true
- # Extract "The next release version is X" for use in summary step
grep -oE "The next release version is [^[:space:]]+" semantic-release.log 2>/dev/null | sed 's/The next release version is //' > next-version.txt || echo "" > next-version.txt
else
- echo "Running semantic-release (REAL release)..."
npx semantic-release
fi
+ # npm publish uses OIDC (id-token: write + --provenance). No NPM_TOKEN needed.
+ # Require on npmjs.com: Package → Package settings → Trusted publishers →
+ # Add: GitHub Actions, org cloudinary-devs, repo create-cloudinary-react, workflow release.yml
+ # npm trusted publishing (OIDC) requires npm CLI 11.5.1+; Node 20 ships with npm 9.x.
+ # Force OIDC-only: override NPM_CONFIG_USERCONFIG so npm ignores setup-node's .npmrc (which may reference a stale token).
- name: Publish to npm using trusted publishing
if: github.event.inputs.dry_run != 'true'
+ env:
+ NODE_AUTH_TOKEN: ''
+ NPM_TOKEN: ''
+ NPM_CONFIG_USERCONFIG: '${{ runner.temp }}/.npmrc-oidc'
run: |
echo "=== Publishing to npm with trusted publishing (OIDC) ==="
-
- # Ensure .npmrc is available (setup-node should have created it)
- if [ -f "$NPM_CONFIG_USERCONFIG" ]; then
- cp "$NPM_CONFIG_USERCONFIG" ~/.npmrc
- echo "✓ Using .npmrc for authentication"
- fi
+ unset NODE_AUTH_TOKEN NPM_TOKEN 2>/dev/null || true
+ # Config that has only registry — no _authToken — so npm uses OIDC
+ echo "registry=https://registry.npmjs.org/" > "$NPM_CONFIG_USERCONFIG"
+ # OIDC for publish requires npm 11.5.1+ (Node 20 ships with npm 9.x)
+ npm install -g npm@latest
+ npm --version
# Get versions
VERSION_BEFORE="${{ steps.version-before.outputs.version }}"
@@ -164,7 +171,8 @@ jobs:
echo "Publishing to npm..."
# Publish using npm publish which supports OIDC/trusted publishing
- npm publish --provenance --access public
+ # --tag latest so installers get the most recent version (npm i create-cloudinary-react / npx create-cloudinary-react)
+ npm publish --provenance --access public --tag latest
echo "✓ Published $VERSION_AFTER to npm"
else
echo "No version change detected (version: $VERSION_AFTER)"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index aa229b6..0a27e20 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,563 @@
+# [1.0.0-beta.21](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.20...v1.0.0-beta.21) (2026-02-25)
+
+
+### Bug Fixes
+
+* update title in readme and ui ([54f633e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/54f633e6fc2a6e800979c9962383c8f83523f79b))
+
+# [1.0.0-beta.20](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.19...v1.0.0-beta.20) (2026-02-23)
+
+
+### Bug Fixes
+
+* Merge pull request [#9](https://github.com/cloudinary-devs/create-cloudinary-react/issues/9) from jlooper-cloudinary/main ([9696686](https://github.com/cloudinary-devs/create-cloudinary-react/commit/96966864d65168b0210a8200f26c9d0d144b6fa9))
+
+# [1.0.0-beta.19](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.18...v1.0.0-beta.19) (2026-02-18)
+
+
+### Bug Fixes
+
+* update readme ([b7a77ca](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b7a77caaf4bf052c68bd912a0ae9c51d424d07cd))
+
+# [1.0.0-beta.18](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.17...v1.0.0-beta.18) (2026-02-18)
+
+
+### Bug Fixes
+
+* update registration link ([2a69d7a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/2a69d7ada9de1cb2751bbd4c1d344422b4c2dd5e))
+* update video player example ([9db39ee](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9db39ee8fc149c748ea422544a79b45a60365f47))
+
+# [1.0.0-beta.17](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.16...v1.0.0-beta.17) (2026-02-12)
+
+
+### Bug Fixes
+
+* simplified questions into arrays ([bade600](https://github.com/cloudinary-devs/create-cloudinary-react/commit/bade600ee63de7a7c3d8eec40569f8896f96a7bd))
+
+
+### Features
+
+* prompt copied animation ([9ad35b2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9ad35b2351a69bca6d4dcd716b10a1508d5228a8))
+
+# [1.0.0-beta.16](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.15...v1.0.0-beta.16) (2026-02-11)
+
+
+### Bug Fixes
+
+* add copy on click ([a4ecf5d](https://github.com/cloudinary-devs/create-cloudinary-react/commit/a4ecf5d1d1478d854683a115735fa71f7fb50cea))
+* complete the sentance ([d6c68dd](https://github.com/cloudinary-devs/create-cloudinary-react/commit/d6c68ddc213fbc18771f26840f33edb805950799))
+* remove upload question ([fbf01ae](https://github.com/cloudinary-devs/create-cloudinary-react/commit/fbf01ae696e158cab48feb053a3515bb21453fdd))
+
+# [1.0.0-beta.15](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.14...v1.0.0-beta.15) (2026-02-10)
+
+
+### Bug Fixes
+
+* use correct CLAUDE.md convention for Claude Code ([07a8a08](https://github.com/cloudinary-devs/create-cloudinary-react/commit/07a8a085686b160387388464a722d90c09cb6805))
+
+
+### Features
+
+* **analytics:** add CLI feature detection for React SDK ([4dfe495](https://github.com/cloudinary-devs/create-cloudinary-react/commit/4dfe4957bf615e9df08afe729e4f024c02d1cc7f))
+
+# [1.0.0-beta.14](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.13...v1.0.0-beta.14) (2026-02-04)
+
+
+### Bug Fixes
+
+* improve upload widget reliability and add video player poster options ([ac51e42](https://github.com/cloudinary-devs/create-cloudinary-react/commit/ac51e420f1b81f2a055bb3fb7e0331841e86b37a))
+
+# [1.0.0-beta.13](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.12...v1.0.0-beta.13) (2026-02-03)
+
+
+### Features
+
+* simplify AI prompts section with clear copy-paste instructions ([2b60415](https://github.com/cloudinary-devs/create-cloudinary-react/commit/2b60415c1d3e23aabbe95b2c5366078857a5b37c))
+
+# [1.0.0-beta.12](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.11...v1.0.0-beta.12) (2026-02-02)
+
+
+### Bug Fixes
+
+* remove console.log ([369e92f](https://github.com/cloudinary-devs/create-cloudinary-react/commit/369e92f3cb3e5af633c1553380119d5a685ec506))
+
+
+### Features
+
+* non-interactive 'headless' mode ([4334991](https://github.com/cloudinary-devs/create-cloudinary-react/commit/4334991f99eddfb3ae2b017c977f9c69c0c500fd))
+
+# [1.0.0-beta.11](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.10...v1.0.0-beta.11) (2026-01-30)
+
+
+### Features
+
+* put Inquirer inputs on a new, separate line ([ba4a26d](https://github.com/cloudinary-devs/create-cloudinary-react/commit/ba4a26d0a84321ef8461babbd0c42bbe1fb77e81))
+
+# [1.0.0-beta.10](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.9...v1.0.0-beta.10) (2026-01-30)
+
+
+### Bug Fixes
+
+* **cli:** show prompt help in message, one line per prompt ([83b8927](https://github.com/cloudinary-devs/create-cloudinary-react/commit/83b8927caee8649cc77457e17951237d96cf5637))
+
+
+### Features
+
+* Add unsigned uploads documentation link ([c13ebc5](https://github.com/cloudinary-devs/create-cloudinary-react/commit/c13ebc5e93e0c343e5540bfd053db794af7305ec))
+
+# [1.0.0-beta.9](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.8...v1.0.0-beta.9) (2026-01-29)
+
+
+### Bug Fixes
+
+* **rules:** video player CSS import — use path without dist/ ([f749f08](https://github.com/cloudinary-devs/create-cloudinary-react/commit/f749f08ef79e365aba1a992a362ca6a0fb42bbe1))
+
+# [1.0.0-beta.8](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.7...v1.0.0-beta.8) (2026-01-28)
+
+
+### Bug Fixes
+
+* publish to latest tag so installers get most recent version ([d907c93](https://github.com/cloudinary-devs/create-cloudinary-react/commit/d907c933a1d0cd7ba83512725f05c453cce77d9f))
+* url-gen imports, overlay rules, and upload widget race condition ([08f06af](https://github.com/cloudinary-devs/create-cloudinary-react/commit/08f06af90ade2f957e3a1997afe352c145c65b82))
+
+# [1.0.0-beta.7](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.6...v1.0.0-beta.7) (2026-01-27)
+
+
+### Bug Fixes
+
+* use --tag beta when publishing prerelease to npm ([9bb888c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9bb888c0f522e30ed49e5fe8e814c80417ed7a0f))
+
+# [1.0.0-beta.6](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.5...v1.0.0-beta.6) (2026-01-27)
+
+
+### Bug Fixes
+
+* use npm 11+ in publish step for OIDC trusted publishing ([a501a0a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/a501a0a4d34397b45eda63a7e622792f7e1095ac))
+
+# [1.0.0-beta.5](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.4...v1.0.0-beta.5) (2026-01-27)
+
+
+### Bug Fixes
+
+* override NPM_CONFIG_USERCONFIG in publish step for OIDC-only ([ced5cc3](https://github.com/cloudinary-devs/create-cloudinary-react/commit/ced5cc32255004846e15bfc77becd5f110e9684c))
+
+# [1.0.0-beta.4](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.3...v1.0.0-beta.4) (2026-01-27)
+
+
+### Bug Fixes
+
+* use OIDC-only for npm publish step ([d06c324](https://github.com/cloudinary-devs/create-cloudinary-react/commit/d06c3245fec9b246fda139fadbd69da54931a170))
+
+# [1.0.0-beta.3](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.2...v1.0.0-beta.3) (2026-01-27)
+
+
+### Bug Fixes
+
+* clarify that new versions are published to npm on release ([e6bf007](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e6bf007601ce3da1edde1c0e5eb1ee089bbffae4))
+
+# [1.0.0-beta.2](https://github.com/cloudinary-devs/create-cloudinary-react/compare/v1.0.0-beta.1...v1.0.0-beta.2) (2026-01-27)
+
+
+### Bug Fixes
+
+* clarify release workflow in README ([de81bee](https://github.com/cloudinary-devs/create-cloudinary-react/commit/de81bee417ce1032aa297dd94c366d26ec9beead))
+
+# 1.0.0-beta.1 (2026-01-27)
+
+
+### Bug Fixes
+
+* add debug output and explicit npm config for semantic-release ([5cec039](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5cec03918105699758405ed8f9909220427be893))
+* add dummy release branch to satisfy semantic-release validation ([cce1e98](https://github.com/cloudinary-devs/create-cloudinary-react/commit/cce1e98c6bb0265630b5f1bf74b1ba0e30c30fc4))
+* add explicit branch checkout and debug info for semantic-release ([241ef66](https://github.com/cloudinary-devs/create-cloudinary-react/commit/241ef66baf8126019b58bfd42f2a19fe710d6c77))
+* add explicit registry URL to npm plugin config ([1fb7968](https://github.com/cloudinary-devs/create-cloudinary-react/commit/1fb7968a97acc0696be74c019a516b590b4e16ad))
+* add initial tag creation and maintenance branch pattern for semantic-release ([0aab3a3](https://github.com/cloudinary-devs/create-cloudinary-react/commit/0aab3a3788db401683284253cf4a63454cf5ce18))
+* add semantic-release note when creating initial tag and when tag exists ([dea082f](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dea082f382e159339b7ba452c497cb8d71a2dca3))
+* check version change before publishing to npm ([c7cb003](https://github.com/cloudinary-devs/create-cloudinary-react/commit/c7cb0032c420c277245f7266830b219e906529c7))
+* configure git user before creating initial tag ([619fcc1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/619fcc1277846026070c03c9a09a16e1dd77fe0e))
+* configure main as both regular and prerelease branch, fetch all branches ([92475e1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/92475e182873b75999d426f692961b34482283a2))
+* configure npm authentication for trusted publishing with semantic-release ([d2ad403](https://github.com/cloudinary-devs/create-cloudinary-react/commit/d2ad40360371fcdc28c7d97c4074dbc383ee7861))
+* extract npm token from .npmrc for semantic-release with trusted publishing ([a408d4c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/a408d4cca11c60d865aacf327ec28bf739de8cb3))
+* improve admin permission check in release workflow ([989b724](https://github.com/cloudinary-devs/create-cloudinary-react/commit/989b72458d5d48fee5c618ac97de84f85cd2c96d))
+* improve git branch setup for semantic-release detection ([86fd306](https://github.com/cloudinary-devs/create-cloudinary-react/commit/86fd306af6e1f6bea1312169dff96e9b5ce223f3))
+* improve npm token extraction with better debugging and fallbacks ([5c9ec06](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5c9ec064cdfc25dd3685fcb6e258e8739ddf1f9a))
+* improve npm token extraction with multiple locations and fallbacks ([5df6779](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5df6779b0820403c1a0fccf222defe58cb68d62e))
+* improve token extraction and .npmrc formatting for npm authentication ([9f18d51](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9f18d511949e1f2342cf41d6fb253d709a3fb6dd))
+* migrate v1.0.0-beta.1 tag for semantic-release (one-time) ([dc21199](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dc2119984ae7eef4a2191a6890e4b8131d0372c6))
+* **release:** add main as regular branch to satisfy semantic-release validation ([beed14e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/beed14e663e2aa61a9e7755c99ea331422680877))
+* **release:** add semantic-release note to existing v1.0.0-beta.1 tag ([3d7d408](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3d7d408d2a5e63dfedf900686016ecd049f8b607))
+* **release:** always move v1.0.0-beta.1 to release commit when present ([9b57cce](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9b57cce0dc9520e19530c96e9073ddbce0509f1c))
+* **release:** correct dry-run next version and branch config ([767dc50](https://github.com/cloudinary-devs/create-cloudinary-react/commit/767dc506a1453737c6fd87fa8c60de0803fd7f7f))
+* **release:** ensure master branch exists for semantic-release validation ([3556b83](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3556b830a73234e2405332c05de7f7514dc0ed2a))
+* **release:** ensure v1.0.0-beta.1 is visible to semantic-release ([b2d9cfc](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b2d9cfc88f6eb586d1499caa037b86358dedaa7b))
+* **release:** prepare v1.0.0-beta.1 in one step and re-fetch before semantic-release ([e8525e0](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e8525e01e03872cfa2d99c97dd5c2799722d73b7))
+* **release:** run v1.0.0-beta.1 prepare inside Release step (real run) ([3b66396](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3b66396312c37d9f98e991901528d41f9cc922b5))
+* remove broken v1.0.0-beta.1 tag so semantic-release can create it (one-time) ([24c83cb](https://github.com/cloudinary-devs/create-cloudinary-react/commit/24c83cb0d470f61bc4cdf5a35b86e912f7aba608))
+* remove duplicate main branch config and improve tag fetching ([ebd8291](https://github.com/cloudinary-devs/create-cloudinary-react/commit/ebd82918fc4c0801d3ce31d8d516e1a77da6676c))
+* remove invalid tarballDir config and improve npm auth setup for semantic-release ([975a278](https://github.com/cloudinary-devs/create-cloudinary-react/commit/975a2785bbd5f03bda736669fdada7fec5599e9a))
+* simplify npm token extraction - use token even if whoami fails ([3d7700a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3d7700a0a29b921b3c881342369d266ff236f700))
+* simplify npm token extraction with multiple fallback methods ([dba9bb2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dba9bb2d1bf0357f95a5ce0f8e2318cb51da55fb))
+* update deployment beta ([3b60ec7](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3b60ec7366b174e35e809453b43da4777ed9b30a))
+* update Node.js version to 20 for semantic-release compatibility ([b1efc7c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b1efc7c4c6d6e5f1452489c34ab79b827ef7cb8f))
+* update release please ([00a932e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/00a932e17be240a983798e4cc4c0b4f628ff6699))
+* update semantic-release branch configuration with channel ([dcf9eab](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dcf9eaba7814e0e75bcaf9029508211282f5fb50))
+* use base version in manifest for release-please compatibility ([854e4f2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/854e4f258a5fdb5aed66e37d81ec1e07703f7d32))
+* use maintenance branch pattern to satisfy semantic-release validation ([e74ff2a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e74ff2aff29caf9b9812ee57d7cc1e9729c84e8a))
+
+
+### Features
+
+* initial release of create-cloudinary-react-vite ([865b5da](https://github.com/cloudinary-devs/create-cloudinary-react/commit/865b5da1ae437d9e47f69bb11f2ff778fc0ca16a))
+* use trusted publishing by disabling npm publish in semantic-release and publishing manually ([e2dbe19](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e2dbe197b45824843777ee26864d113ae7fadf27))
+
+# 1.0.0-beta.1 (2026-01-27)
+
+
+### Bug Fixes
+
+* add debug output and explicit npm config for semantic-release ([5cec039](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5cec03918105699758405ed8f9909220427be893))
+* add dummy release branch to satisfy semantic-release validation ([cce1e98](https://github.com/cloudinary-devs/create-cloudinary-react/commit/cce1e98c6bb0265630b5f1bf74b1ba0e30c30fc4))
+* add explicit branch checkout and debug info for semantic-release ([241ef66](https://github.com/cloudinary-devs/create-cloudinary-react/commit/241ef66baf8126019b58bfd42f2a19fe710d6c77))
+* add explicit registry URL to npm plugin config ([1fb7968](https://github.com/cloudinary-devs/create-cloudinary-react/commit/1fb7968a97acc0696be74c019a516b590b4e16ad))
+* add initial tag creation and maintenance branch pattern for semantic-release ([0aab3a3](https://github.com/cloudinary-devs/create-cloudinary-react/commit/0aab3a3788db401683284253cf4a63454cf5ce18))
+* add semantic-release note when creating initial tag and when tag exists ([dea082f](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dea082f382e159339b7ba452c497cb8d71a2dca3))
+* check version change before publishing to npm ([c7cb003](https://github.com/cloudinary-devs/create-cloudinary-react/commit/c7cb0032c420c277245f7266830b219e906529c7))
+* configure git user before creating initial tag ([619fcc1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/619fcc1277846026070c03c9a09a16e1dd77fe0e))
+* configure main as both regular and prerelease branch, fetch all branches ([92475e1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/92475e182873b75999d426f692961b34482283a2))
+* configure npm authentication for trusted publishing with semantic-release ([d2ad403](https://github.com/cloudinary-devs/create-cloudinary-react/commit/d2ad40360371fcdc28c7d97c4074dbc383ee7861))
+* extract npm token from .npmrc for semantic-release with trusted publishing ([a408d4c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/a408d4cca11c60d865aacf327ec28bf739de8cb3))
+* improve admin permission check in release workflow ([989b724](https://github.com/cloudinary-devs/create-cloudinary-react/commit/989b72458d5d48fee5c618ac97de84f85cd2c96d))
+* improve git branch setup for semantic-release detection ([86fd306](https://github.com/cloudinary-devs/create-cloudinary-react/commit/86fd306af6e1f6bea1312169dff96e9b5ce223f3))
+* improve npm token extraction with better debugging and fallbacks ([5c9ec06](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5c9ec064cdfc25dd3685fcb6e258e8739ddf1f9a))
+* improve npm token extraction with multiple locations and fallbacks ([5df6779](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5df6779b0820403c1a0fccf222defe58cb68d62e))
+* improve token extraction and .npmrc formatting for npm authentication ([9f18d51](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9f18d511949e1f2342cf41d6fb253d709a3fb6dd))
+* migrate v1.0.0-beta.1 tag for semantic-release (one-time) ([dc21199](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dc2119984ae7eef4a2191a6890e4b8131d0372c6))
+* **release:** add main as regular branch to satisfy semantic-release validation ([beed14e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/beed14e663e2aa61a9e7755c99ea331422680877))
+* **release:** add semantic-release note to existing v1.0.0-beta.1 tag ([3d7d408](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3d7d408d2a5e63dfedf900686016ecd049f8b607))
+* **release:** always move v1.0.0-beta.1 to release commit when present ([9b57cce](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9b57cce0dc9520e19530c96e9073ddbce0509f1c))
+* **release:** correct dry-run next version and branch config ([767dc50](https://github.com/cloudinary-devs/create-cloudinary-react/commit/767dc506a1453737c6fd87fa8c60de0803fd7f7f))
+* **release:** ensure master branch exists for semantic-release validation ([3556b83](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3556b830a73234e2405332c05de7f7514dc0ed2a))
+* **release:** ensure v1.0.0-beta.1 is visible to semantic-release ([b2d9cfc](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b2d9cfc88f6eb586d1499caa037b86358dedaa7b))
+* **release:** prepare v1.0.0-beta.1 in one step and re-fetch before semantic-release ([e8525e0](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e8525e01e03872cfa2d99c97dd5c2799722d73b7))
+* **release:** run v1.0.0-beta.1 prepare inside Release step (real run) ([3b66396](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3b66396312c37d9f98e991901528d41f9cc922b5))
+* remove duplicate main branch config and improve tag fetching ([ebd8291](https://github.com/cloudinary-devs/create-cloudinary-react/commit/ebd82918fc4c0801d3ce31d8d516e1a77da6676c))
+* remove invalid tarballDir config and improve npm auth setup for semantic-release ([975a278](https://github.com/cloudinary-devs/create-cloudinary-react/commit/975a2785bbd5f03bda736669fdada7fec5599e9a))
+* simplify npm token extraction - use token even if whoami fails ([3d7700a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3d7700a0a29b921b3c881342369d266ff236f700))
+* simplify npm token extraction with multiple fallback methods ([dba9bb2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dba9bb2d1bf0357f95a5ce0f8e2318cb51da55fb))
+* update deployment beta ([3b60ec7](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3b60ec7366b174e35e809453b43da4777ed9b30a))
+* update Node.js version to 20 for semantic-release compatibility ([b1efc7c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b1efc7c4c6d6e5f1452489c34ab79b827ef7cb8f))
+* update release please ([00a932e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/00a932e17be240a983798e4cc4c0b4f628ff6699))
+* update semantic-release branch configuration with channel ([dcf9eab](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dcf9eaba7814e0e75bcaf9029508211282f5fb50))
+* use base version in manifest for release-please compatibility ([854e4f2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/854e4f258a5fdb5aed66e37d81ec1e07703f7d32))
+* use maintenance branch pattern to satisfy semantic-release validation ([e74ff2a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e74ff2aff29caf9b9812ee57d7cc1e9729c84e8a))
+
+
+### Features
+
+* initial release of create-cloudinary-react-vite ([865b5da](https://github.com/cloudinary-devs/create-cloudinary-react/commit/865b5da1ae437d9e47f69bb11f2ff778fc0ca16a))
+* use trusted publishing by disabling npm publish in semantic-release and publishing manually ([e2dbe19](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e2dbe197b45824843777ee26864d113ae7fadf27))
+
+# 1.0.0-beta.1 (2026-01-27)
+
+
+### Bug Fixes
+
+* add debug output and explicit npm config for semantic-release ([5cec039](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5cec03918105699758405ed8f9909220427be893))
+* add dummy release branch to satisfy semantic-release validation ([cce1e98](https://github.com/cloudinary-devs/create-cloudinary-react/commit/cce1e98c6bb0265630b5f1bf74b1ba0e30c30fc4))
+* add explicit branch checkout and debug info for semantic-release ([241ef66](https://github.com/cloudinary-devs/create-cloudinary-react/commit/241ef66baf8126019b58bfd42f2a19fe710d6c77))
+* add explicit registry URL to npm plugin config ([1fb7968](https://github.com/cloudinary-devs/create-cloudinary-react/commit/1fb7968a97acc0696be74c019a516b590b4e16ad))
+* add initial tag creation and maintenance branch pattern for semantic-release ([0aab3a3](https://github.com/cloudinary-devs/create-cloudinary-react/commit/0aab3a3788db401683284253cf4a63454cf5ce18))
+* add semantic-release note when creating initial tag and when tag exists ([dea082f](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dea082f382e159339b7ba452c497cb8d71a2dca3))
+* check version change before publishing to npm ([c7cb003](https://github.com/cloudinary-devs/create-cloudinary-react/commit/c7cb0032c420c277245f7266830b219e906529c7))
+* configure git user before creating initial tag ([619fcc1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/619fcc1277846026070c03c9a09a16e1dd77fe0e))
+* configure main as both regular and prerelease branch, fetch all branches ([92475e1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/92475e182873b75999d426f692961b34482283a2))
+* configure npm authentication for trusted publishing with semantic-release ([d2ad403](https://github.com/cloudinary-devs/create-cloudinary-react/commit/d2ad40360371fcdc28c7d97c4074dbc383ee7861))
+* extract npm token from .npmrc for semantic-release with trusted publishing ([a408d4c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/a408d4cca11c60d865aacf327ec28bf739de8cb3))
+* improve admin permission check in release workflow ([989b724](https://github.com/cloudinary-devs/create-cloudinary-react/commit/989b72458d5d48fee5c618ac97de84f85cd2c96d))
+* improve git branch setup for semantic-release detection ([86fd306](https://github.com/cloudinary-devs/create-cloudinary-react/commit/86fd306af6e1f6bea1312169dff96e9b5ce223f3))
+* improve npm token extraction with better debugging and fallbacks ([5c9ec06](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5c9ec064cdfc25dd3685fcb6e258e8739ddf1f9a))
+* improve npm token extraction with multiple locations and fallbacks ([5df6779](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5df6779b0820403c1a0fccf222defe58cb68d62e))
+* improve token extraction and .npmrc formatting for npm authentication ([9f18d51](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9f18d511949e1f2342cf41d6fb253d709a3fb6dd))
+* **release:** add main as regular branch to satisfy semantic-release validation ([beed14e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/beed14e663e2aa61a9e7755c99ea331422680877))
+* **release:** add semantic-release note to existing v1.0.0-beta.1 tag ([3d7d408](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3d7d408d2a5e63dfedf900686016ecd049f8b607))
+* **release:** always move v1.0.0-beta.1 to release commit when present ([9b57cce](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9b57cce0dc9520e19530c96e9073ddbce0509f1c))
+* **release:** correct dry-run next version and branch config ([767dc50](https://github.com/cloudinary-devs/create-cloudinary-react/commit/767dc506a1453737c6fd87fa8c60de0803fd7f7f))
+* **release:** ensure master branch exists for semantic-release validation ([3556b83](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3556b830a73234e2405332c05de7f7514dc0ed2a))
+* **release:** ensure v1.0.0-beta.1 is visible to semantic-release ([b2d9cfc](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b2d9cfc88f6eb586d1499caa037b86358dedaa7b))
+* **release:** prepare v1.0.0-beta.1 in one step and re-fetch before semantic-release ([e8525e0](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e8525e01e03872cfa2d99c97dd5c2799722d73b7))
+* **release:** run v1.0.0-beta.1 prepare inside Release step (real run) ([3b66396](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3b66396312c37d9f98e991901528d41f9cc922b5))
+* remove duplicate main branch config and improve tag fetching ([ebd8291](https://github.com/cloudinary-devs/create-cloudinary-react/commit/ebd82918fc4c0801d3ce31d8d516e1a77da6676c))
+* remove invalid tarballDir config and improve npm auth setup for semantic-release ([975a278](https://github.com/cloudinary-devs/create-cloudinary-react/commit/975a2785bbd5f03bda736669fdada7fec5599e9a))
+* simplify npm token extraction - use token even if whoami fails ([3d7700a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3d7700a0a29b921b3c881342369d266ff236f700))
+* simplify npm token extraction with multiple fallback methods ([dba9bb2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dba9bb2d1bf0357f95a5ce0f8e2318cb51da55fb))
+* update deployment beta ([3b60ec7](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3b60ec7366b174e35e809453b43da4777ed9b30a))
+* update Node.js version to 20 for semantic-release compatibility ([b1efc7c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b1efc7c4c6d6e5f1452489c34ab79b827ef7cb8f))
+* update release please ([00a932e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/00a932e17be240a983798e4cc4c0b4f628ff6699))
+* update semantic-release branch configuration with channel ([dcf9eab](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dcf9eaba7814e0e75bcaf9029508211282f5fb50))
+* use base version in manifest for release-please compatibility ([854e4f2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/854e4f258a5fdb5aed66e37d81ec1e07703f7d32))
+* use maintenance branch pattern to satisfy semantic-release validation ([e74ff2a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e74ff2aff29caf9b9812ee57d7cc1e9729c84e8a))
+
+
+### Features
+
+* initial release of create-cloudinary-react-vite ([865b5da](https://github.com/cloudinary-devs/create-cloudinary-react/commit/865b5da1ae437d9e47f69bb11f2ff778fc0ca16a))
+* use trusted publishing by disabling npm publish in semantic-release and publishing manually ([e2dbe19](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e2dbe197b45824843777ee26864d113ae7fadf27))
+
+# 1.0.0-beta.1 (2026-01-27)
+
+
+### Bug Fixes
+
+* add debug output and explicit npm config for semantic-release ([5cec039](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5cec03918105699758405ed8f9909220427be893))
+* add dummy release branch to satisfy semantic-release validation ([cce1e98](https://github.com/cloudinary-devs/create-cloudinary-react/commit/cce1e98c6bb0265630b5f1bf74b1ba0e30c30fc4))
+* add explicit branch checkout and debug info for semantic-release ([241ef66](https://github.com/cloudinary-devs/create-cloudinary-react/commit/241ef66baf8126019b58bfd42f2a19fe710d6c77))
+* add explicit registry URL to npm plugin config ([1fb7968](https://github.com/cloudinary-devs/create-cloudinary-react/commit/1fb7968a97acc0696be74c019a516b590b4e16ad))
+* add initial tag creation and maintenance branch pattern for semantic-release ([0aab3a3](https://github.com/cloudinary-devs/create-cloudinary-react/commit/0aab3a3788db401683284253cf4a63454cf5ce18))
+* check version change before publishing to npm ([c7cb003](https://github.com/cloudinary-devs/create-cloudinary-react/commit/c7cb0032c420c277245f7266830b219e906529c7))
+* configure git user before creating initial tag ([619fcc1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/619fcc1277846026070c03c9a09a16e1dd77fe0e))
+* configure main as both regular and prerelease branch, fetch all branches ([92475e1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/92475e182873b75999d426f692961b34482283a2))
+* configure npm authentication for trusted publishing with semantic-release ([d2ad403](https://github.com/cloudinary-devs/create-cloudinary-react/commit/d2ad40360371fcdc28c7d97c4074dbc383ee7861))
+* extract npm token from .npmrc for semantic-release with trusted publishing ([a408d4c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/a408d4cca11c60d865aacf327ec28bf739de8cb3))
+* improve admin permission check in release workflow ([989b724](https://github.com/cloudinary-devs/create-cloudinary-react/commit/989b72458d5d48fee5c618ac97de84f85cd2c96d))
+* improve git branch setup for semantic-release detection ([86fd306](https://github.com/cloudinary-devs/create-cloudinary-react/commit/86fd306af6e1f6bea1312169dff96e9b5ce223f3))
+* improve npm token extraction with better debugging and fallbacks ([5c9ec06](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5c9ec064cdfc25dd3685fcb6e258e8739ddf1f9a))
+* improve npm token extraction with multiple locations and fallbacks ([5df6779](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5df6779b0820403c1a0fccf222defe58cb68d62e))
+* improve token extraction and .npmrc formatting for npm authentication ([9f18d51](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9f18d511949e1f2342cf41d6fb253d709a3fb6dd))
+* **release:** add main as regular branch to satisfy semantic-release validation ([beed14e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/beed14e663e2aa61a9e7755c99ea331422680877))
+* **release:** add semantic-release note to existing v1.0.0-beta.1 tag ([3d7d408](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3d7d408d2a5e63dfedf900686016ecd049f8b607))
+* **release:** always move v1.0.0-beta.1 to release commit when present ([9b57cce](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9b57cce0dc9520e19530c96e9073ddbce0509f1c))
+* **release:** correct dry-run next version and branch config ([767dc50](https://github.com/cloudinary-devs/create-cloudinary-react/commit/767dc506a1453737c6fd87fa8c60de0803fd7f7f))
+* **release:** ensure master branch exists for semantic-release validation ([3556b83](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3556b830a73234e2405332c05de7f7514dc0ed2a))
+* **release:** ensure v1.0.0-beta.1 is visible to semantic-release ([b2d9cfc](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b2d9cfc88f6eb586d1499caa037b86358dedaa7b))
+* **release:** prepare v1.0.0-beta.1 in one step and re-fetch before semantic-release ([e8525e0](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e8525e01e03872cfa2d99c97dd5c2799722d73b7))
+* **release:** run v1.0.0-beta.1 prepare inside Release step (real run) ([3b66396](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3b66396312c37d9f98e991901528d41f9cc922b5))
+* remove duplicate main branch config and improve tag fetching ([ebd8291](https://github.com/cloudinary-devs/create-cloudinary-react/commit/ebd82918fc4c0801d3ce31d8d516e1a77da6676c))
+* remove invalid tarballDir config and improve npm auth setup for semantic-release ([975a278](https://github.com/cloudinary-devs/create-cloudinary-react/commit/975a2785bbd5f03bda736669fdada7fec5599e9a))
+* simplify npm token extraction - use token even if whoami fails ([3d7700a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3d7700a0a29b921b3c881342369d266ff236f700))
+* simplify npm token extraction with multiple fallback methods ([dba9bb2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dba9bb2d1bf0357f95a5ce0f8e2318cb51da55fb))
+* update deployment beta ([3b60ec7](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3b60ec7366b174e35e809453b43da4777ed9b30a))
+* update Node.js version to 20 for semantic-release compatibility ([b1efc7c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b1efc7c4c6d6e5f1452489c34ab79b827ef7cb8f))
+* update release please ([00a932e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/00a932e17be240a983798e4cc4c0b4f628ff6699))
+* update semantic-release branch configuration with channel ([dcf9eab](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dcf9eaba7814e0e75bcaf9029508211282f5fb50))
+* use base version in manifest for release-please compatibility ([854e4f2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/854e4f258a5fdb5aed66e37d81ec1e07703f7d32))
+* use maintenance branch pattern to satisfy semantic-release validation ([e74ff2a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e74ff2aff29caf9b9812ee57d7cc1e9729c84e8a))
+
+
+### Features
+
+* initial release of create-cloudinary-react-vite ([865b5da](https://github.com/cloudinary-devs/create-cloudinary-react/commit/865b5da1ae437d9e47f69bb11f2ff778fc0ca16a))
+* use trusted publishing by disabling npm publish in semantic-release and publishing manually ([e2dbe19](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e2dbe197b45824843777ee26864d113ae7fadf27))
+
+# 1.0.0-beta.1 (2026-01-27)
+
+
+### Bug Fixes
+
+* add debug output and explicit npm config for semantic-release ([5cec039](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5cec03918105699758405ed8f9909220427be893))
+* add dummy release branch to satisfy semantic-release validation ([cce1e98](https://github.com/cloudinary-devs/create-cloudinary-react/commit/cce1e98c6bb0265630b5f1bf74b1ba0e30c30fc4))
+* add explicit branch checkout and debug info for semantic-release ([241ef66](https://github.com/cloudinary-devs/create-cloudinary-react/commit/241ef66baf8126019b58bfd42f2a19fe710d6c77))
+* add explicit registry URL to npm plugin config ([1fb7968](https://github.com/cloudinary-devs/create-cloudinary-react/commit/1fb7968a97acc0696be74c019a516b590b4e16ad))
+* add initial tag creation and maintenance branch pattern for semantic-release ([0aab3a3](https://github.com/cloudinary-devs/create-cloudinary-react/commit/0aab3a3788db401683284253cf4a63454cf5ce18))
+* check version change before publishing to npm ([c7cb003](https://github.com/cloudinary-devs/create-cloudinary-react/commit/c7cb0032c420c277245f7266830b219e906529c7))
+* configure git user before creating initial tag ([619fcc1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/619fcc1277846026070c03c9a09a16e1dd77fe0e))
+* configure main as both regular and prerelease branch, fetch all branches ([92475e1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/92475e182873b75999d426f692961b34482283a2))
+* configure npm authentication for trusted publishing with semantic-release ([d2ad403](https://github.com/cloudinary-devs/create-cloudinary-react/commit/d2ad40360371fcdc28c7d97c4074dbc383ee7861))
+* extract npm token from .npmrc for semantic-release with trusted publishing ([a408d4c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/a408d4cca11c60d865aacf327ec28bf739de8cb3))
+* improve admin permission check in release workflow ([989b724](https://github.com/cloudinary-devs/create-cloudinary-react/commit/989b72458d5d48fee5c618ac97de84f85cd2c96d))
+* improve git branch setup for semantic-release detection ([86fd306](https://github.com/cloudinary-devs/create-cloudinary-react/commit/86fd306af6e1f6bea1312169dff96e9b5ce223f3))
+* improve npm token extraction with better debugging and fallbacks ([5c9ec06](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5c9ec064cdfc25dd3685fcb6e258e8739ddf1f9a))
+* improve npm token extraction with multiple locations and fallbacks ([5df6779](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5df6779b0820403c1a0fccf222defe58cb68d62e))
+* improve token extraction and .npmrc formatting for npm authentication ([9f18d51](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9f18d511949e1f2342cf41d6fb253d709a3fb6dd))
+* **release:** add main as regular branch to satisfy semantic-release validation ([beed14e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/beed14e663e2aa61a9e7755c99ea331422680877))
+* **release:** add semantic-release note to existing v1.0.0-beta.1 tag ([3d7d408](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3d7d408d2a5e63dfedf900686016ecd049f8b607))
+* **release:** always move v1.0.0-beta.1 to release commit when present ([9b57cce](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9b57cce0dc9520e19530c96e9073ddbce0509f1c))
+* **release:** correct dry-run next version and branch config ([767dc50](https://github.com/cloudinary-devs/create-cloudinary-react/commit/767dc506a1453737c6fd87fa8c60de0803fd7f7f))
+* **release:** ensure master branch exists for semantic-release validation ([3556b83](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3556b830a73234e2405332c05de7f7514dc0ed2a))
+* **release:** ensure v1.0.0-beta.1 is visible to semantic-release ([b2d9cfc](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b2d9cfc88f6eb586d1499caa037b86358dedaa7b))
+* **release:** prepare v1.0.0-beta.1 in one step and re-fetch before semantic-release ([e8525e0](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e8525e01e03872cfa2d99c97dd5c2799722d73b7))
+* remove duplicate main branch config and improve tag fetching ([ebd8291](https://github.com/cloudinary-devs/create-cloudinary-react/commit/ebd82918fc4c0801d3ce31d8d516e1a77da6676c))
+* remove invalid tarballDir config and improve npm auth setup for semantic-release ([975a278](https://github.com/cloudinary-devs/create-cloudinary-react/commit/975a2785bbd5f03bda736669fdada7fec5599e9a))
+* simplify npm token extraction - use token even if whoami fails ([3d7700a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3d7700a0a29b921b3c881342369d266ff236f700))
+* simplify npm token extraction with multiple fallback methods ([dba9bb2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dba9bb2d1bf0357f95a5ce0f8e2318cb51da55fb))
+* update deployment beta ([3b60ec7](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3b60ec7366b174e35e809453b43da4777ed9b30a))
+* update Node.js version to 20 for semantic-release compatibility ([b1efc7c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b1efc7c4c6d6e5f1452489c34ab79b827ef7cb8f))
+* update release please ([00a932e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/00a932e17be240a983798e4cc4c0b4f628ff6699))
+* update semantic-release branch configuration with channel ([dcf9eab](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dcf9eaba7814e0e75bcaf9029508211282f5fb50))
+* use base version in manifest for release-please compatibility ([854e4f2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/854e4f258a5fdb5aed66e37d81ec1e07703f7d32))
+* use maintenance branch pattern to satisfy semantic-release validation ([e74ff2a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e74ff2aff29caf9b9812ee57d7cc1e9729c84e8a))
+
+
+### Features
+
+* initial release of create-cloudinary-react-vite ([865b5da](https://github.com/cloudinary-devs/create-cloudinary-react/commit/865b5da1ae437d9e47f69bb11f2ff778fc0ca16a))
+* use trusted publishing by disabling npm publish in semantic-release and publishing manually ([e2dbe19](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e2dbe197b45824843777ee26864d113ae7fadf27))
+
+# 1.0.0-beta.1 (2026-01-27)
+
+
+### Bug Fixes
+
+* add debug output and explicit npm config for semantic-release ([5cec039](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5cec03918105699758405ed8f9909220427be893))
+* add dummy release branch to satisfy semantic-release validation ([cce1e98](https://github.com/cloudinary-devs/create-cloudinary-react/commit/cce1e98c6bb0265630b5f1bf74b1ba0e30c30fc4))
+* add explicit branch checkout and debug info for semantic-release ([241ef66](https://github.com/cloudinary-devs/create-cloudinary-react/commit/241ef66baf8126019b58bfd42f2a19fe710d6c77))
+* add explicit registry URL to npm plugin config ([1fb7968](https://github.com/cloudinary-devs/create-cloudinary-react/commit/1fb7968a97acc0696be74c019a516b590b4e16ad))
+* add initial tag creation and maintenance branch pattern for semantic-release ([0aab3a3](https://github.com/cloudinary-devs/create-cloudinary-react/commit/0aab3a3788db401683284253cf4a63454cf5ce18))
+* check version change before publishing to npm ([c7cb003](https://github.com/cloudinary-devs/create-cloudinary-react/commit/c7cb0032c420c277245f7266830b219e906529c7))
+* configure git user before creating initial tag ([619fcc1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/619fcc1277846026070c03c9a09a16e1dd77fe0e))
+* configure main as both regular and prerelease branch, fetch all branches ([92475e1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/92475e182873b75999d426f692961b34482283a2))
+* configure npm authentication for trusted publishing with semantic-release ([d2ad403](https://github.com/cloudinary-devs/create-cloudinary-react/commit/d2ad40360371fcdc28c7d97c4074dbc383ee7861))
+* extract npm token from .npmrc for semantic-release with trusted publishing ([a408d4c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/a408d4cca11c60d865aacf327ec28bf739de8cb3))
+* improve admin permission check in release workflow ([989b724](https://github.com/cloudinary-devs/create-cloudinary-react/commit/989b72458d5d48fee5c618ac97de84f85cd2c96d))
+* improve git branch setup for semantic-release detection ([86fd306](https://github.com/cloudinary-devs/create-cloudinary-react/commit/86fd306af6e1f6bea1312169dff96e9b5ce223f3))
+* improve npm token extraction with better debugging and fallbacks ([5c9ec06](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5c9ec064cdfc25dd3685fcb6e258e8739ddf1f9a))
+* improve npm token extraction with multiple locations and fallbacks ([5df6779](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5df6779b0820403c1a0fccf222defe58cb68d62e))
+* improve token extraction and .npmrc formatting for npm authentication ([9f18d51](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9f18d511949e1f2342cf41d6fb253d709a3fb6dd))
+* **release:** add main as regular branch to satisfy semantic-release validation ([beed14e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/beed14e663e2aa61a9e7755c99ea331422680877))
+* **release:** add semantic-release note to existing v1.0.0-beta.1 tag ([3d7d408](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3d7d408d2a5e63dfedf900686016ecd049f8b607))
+* **release:** always move v1.0.0-beta.1 to release commit when present ([9b57cce](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9b57cce0dc9520e19530c96e9073ddbce0509f1c))
+* **release:** correct dry-run next version and branch config ([767dc50](https://github.com/cloudinary-devs/create-cloudinary-react/commit/767dc506a1453737c6fd87fa8c60de0803fd7f7f))
+* **release:** ensure master branch exists for semantic-release validation ([3556b83](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3556b830a73234e2405332c05de7f7514dc0ed2a))
+* **release:** ensure v1.0.0-beta.1 is visible to semantic-release ([b2d9cfc](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b2d9cfc88f6eb586d1499caa037b86358dedaa7b))
+* remove duplicate main branch config and improve tag fetching ([ebd8291](https://github.com/cloudinary-devs/create-cloudinary-react/commit/ebd82918fc4c0801d3ce31d8d516e1a77da6676c))
+* remove invalid tarballDir config and improve npm auth setup for semantic-release ([975a278](https://github.com/cloudinary-devs/create-cloudinary-react/commit/975a2785bbd5f03bda736669fdada7fec5599e9a))
+* simplify npm token extraction - use token even if whoami fails ([3d7700a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3d7700a0a29b921b3c881342369d266ff236f700))
+* simplify npm token extraction with multiple fallback methods ([dba9bb2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dba9bb2d1bf0357f95a5ce0f8e2318cb51da55fb))
+* update deployment beta ([3b60ec7](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3b60ec7366b174e35e809453b43da4777ed9b30a))
+* update Node.js version to 20 for semantic-release compatibility ([b1efc7c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b1efc7c4c6d6e5f1452489c34ab79b827ef7cb8f))
+* update release please ([00a932e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/00a932e17be240a983798e4cc4c0b4f628ff6699))
+* update semantic-release branch configuration with channel ([dcf9eab](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dcf9eaba7814e0e75bcaf9029508211282f5fb50))
+* use base version in manifest for release-please compatibility ([854e4f2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/854e4f258a5fdb5aed66e37d81ec1e07703f7d32))
+* use maintenance branch pattern to satisfy semantic-release validation ([e74ff2a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e74ff2aff29caf9b9812ee57d7cc1e9729c84e8a))
+
+
+### Features
+
+* initial release of create-cloudinary-react-vite ([865b5da](https://github.com/cloudinary-devs/create-cloudinary-react/commit/865b5da1ae437d9e47f69bb11f2ff778fc0ca16a))
+* use trusted publishing by disabling npm publish in semantic-release and publishing manually ([e2dbe19](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e2dbe197b45824843777ee26864d113ae7fadf27))
+
+# 1.0.0-beta.1 (2026-01-27)
+
+
+### Bug Fixes
+
+* add debug output and explicit npm config for semantic-release ([5cec039](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5cec03918105699758405ed8f9909220427be893))
+* add dummy release branch to satisfy semantic-release validation ([cce1e98](https://github.com/cloudinary-devs/create-cloudinary-react/commit/cce1e98c6bb0265630b5f1bf74b1ba0e30c30fc4))
+* add explicit branch checkout and debug info for semantic-release ([241ef66](https://github.com/cloudinary-devs/create-cloudinary-react/commit/241ef66baf8126019b58bfd42f2a19fe710d6c77))
+* add explicit registry URL to npm plugin config ([1fb7968](https://github.com/cloudinary-devs/create-cloudinary-react/commit/1fb7968a97acc0696be74c019a516b590b4e16ad))
+* add initial tag creation and maintenance branch pattern for semantic-release ([0aab3a3](https://github.com/cloudinary-devs/create-cloudinary-react/commit/0aab3a3788db401683284253cf4a63454cf5ce18))
+* check version change before publishing to npm ([c7cb003](https://github.com/cloudinary-devs/create-cloudinary-react/commit/c7cb0032c420c277245f7266830b219e906529c7))
+* configure git user before creating initial tag ([619fcc1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/619fcc1277846026070c03c9a09a16e1dd77fe0e))
+* configure main as both regular and prerelease branch, fetch all branches ([92475e1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/92475e182873b75999d426f692961b34482283a2))
+* configure npm authentication for trusted publishing with semantic-release ([d2ad403](https://github.com/cloudinary-devs/create-cloudinary-react/commit/d2ad40360371fcdc28c7d97c4074dbc383ee7861))
+* extract npm token from .npmrc for semantic-release with trusted publishing ([a408d4c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/a408d4cca11c60d865aacf327ec28bf739de8cb3))
+* improve admin permission check in release workflow ([989b724](https://github.com/cloudinary-devs/create-cloudinary-react/commit/989b72458d5d48fee5c618ac97de84f85cd2c96d))
+* improve git branch setup for semantic-release detection ([86fd306](https://github.com/cloudinary-devs/create-cloudinary-react/commit/86fd306af6e1f6bea1312169dff96e9b5ce223f3))
+* improve npm token extraction with better debugging and fallbacks ([5c9ec06](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5c9ec064cdfc25dd3685fcb6e258e8739ddf1f9a))
+* improve npm token extraction with multiple locations and fallbacks ([5df6779](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5df6779b0820403c1a0fccf222defe58cb68d62e))
+* improve token extraction and .npmrc formatting for npm authentication ([9f18d51](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9f18d511949e1f2342cf41d6fb253d709a3fb6dd))
+* **release:** add main as regular branch to satisfy semantic-release validation ([beed14e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/beed14e663e2aa61a9e7755c99ea331422680877))
+* **release:** add semantic-release note to existing v1.0.0-beta.1 tag ([3d7d408](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3d7d408d2a5e63dfedf900686016ecd049f8b607))
+* **release:** correct dry-run next version and branch config ([767dc50](https://github.com/cloudinary-devs/create-cloudinary-react/commit/767dc506a1453737c6fd87fa8c60de0803fd7f7f))
+* **release:** ensure master branch exists for semantic-release validation ([3556b83](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3556b830a73234e2405332c05de7f7514dc0ed2a))
+* **release:** ensure v1.0.0-beta.1 is visible to semantic-release ([b2d9cfc](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b2d9cfc88f6eb586d1499caa037b86358dedaa7b))
+* remove duplicate main branch config and improve tag fetching ([ebd8291](https://github.com/cloudinary-devs/create-cloudinary-react/commit/ebd82918fc4c0801d3ce31d8d516e1a77da6676c))
+* remove invalid tarballDir config and improve npm auth setup for semantic-release ([975a278](https://github.com/cloudinary-devs/create-cloudinary-react/commit/975a2785bbd5f03bda736669fdada7fec5599e9a))
+* simplify npm token extraction - use token even if whoami fails ([3d7700a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3d7700a0a29b921b3c881342369d266ff236f700))
+* simplify npm token extraction with multiple fallback methods ([dba9bb2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dba9bb2d1bf0357f95a5ce0f8e2318cb51da55fb))
+* update deployment beta ([3b60ec7](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3b60ec7366b174e35e809453b43da4777ed9b30a))
+* update Node.js version to 20 for semantic-release compatibility ([b1efc7c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b1efc7c4c6d6e5f1452489c34ab79b827ef7cb8f))
+* update release please ([00a932e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/00a932e17be240a983798e4cc4c0b4f628ff6699))
+* update semantic-release branch configuration with channel ([dcf9eab](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dcf9eaba7814e0e75bcaf9029508211282f5fb50))
+* use base version in manifest for release-please compatibility ([854e4f2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/854e4f258a5fdb5aed66e37d81ec1e07703f7d32))
+* use maintenance branch pattern to satisfy semantic-release validation ([e74ff2a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e74ff2aff29caf9b9812ee57d7cc1e9729c84e8a))
+
+
+### Features
+
+* initial release of create-cloudinary-react-vite ([865b5da](https://github.com/cloudinary-devs/create-cloudinary-react/commit/865b5da1ae437d9e47f69bb11f2ff778fc0ca16a))
+* use trusted publishing by disabling npm publish in semantic-release and publishing manually ([e2dbe19](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e2dbe197b45824843777ee26864d113ae7fadf27))
+
+# 1.0.0-beta.1 (2026-01-27)
+
+
+### Bug Fixes
+
+* add debug output and explicit npm config for semantic-release ([5cec039](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5cec03918105699758405ed8f9909220427be893))
+* add dummy release branch to satisfy semantic-release validation ([cce1e98](https://github.com/cloudinary-devs/create-cloudinary-react/commit/cce1e98c6bb0265630b5f1bf74b1ba0e30c30fc4))
+* add explicit branch checkout and debug info for semantic-release ([241ef66](https://github.com/cloudinary-devs/create-cloudinary-react/commit/241ef66baf8126019b58bfd42f2a19fe710d6c77))
+* add explicit registry URL to npm plugin config ([1fb7968](https://github.com/cloudinary-devs/create-cloudinary-react/commit/1fb7968a97acc0696be74c019a516b590b4e16ad))
+* add initial tag creation and maintenance branch pattern for semantic-release ([0aab3a3](https://github.com/cloudinary-devs/create-cloudinary-react/commit/0aab3a3788db401683284253cf4a63454cf5ce18))
+* check version change before publishing to npm ([c7cb003](https://github.com/cloudinary-devs/create-cloudinary-react/commit/c7cb0032c420c277245f7266830b219e906529c7))
+* configure git user before creating initial tag ([619fcc1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/619fcc1277846026070c03c9a09a16e1dd77fe0e))
+* configure main as both regular and prerelease branch, fetch all branches ([92475e1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/92475e182873b75999d426f692961b34482283a2))
+* configure npm authentication for trusted publishing with semantic-release ([d2ad403](https://github.com/cloudinary-devs/create-cloudinary-react/commit/d2ad40360371fcdc28c7d97c4074dbc383ee7861))
+* extract npm token from .npmrc for semantic-release with trusted publishing ([a408d4c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/a408d4cca11c60d865aacf327ec28bf739de8cb3))
+* improve admin permission check in release workflow ([989b724](https://github.com/cloudinary-devs/create-cloudinary-react/commit/989b72458d5d48fee5c618ac97de84f85cd2c96d))
+* improve git branch setup for semantic-release detection ([86fd306](https://github.com/cloudinary-devs/create-cloudinary-react/commit/86fd306af6e1f6bea1312169dff96e9b5ce223f3))
+* improve npm token extraction with better debugging and fallbacks ([5c9ec06](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5c9ec064cdfc25dd3685fcb6e258e8739ddf1f9a))
+* improve npm token extraction with multiple locations and fallbacks ([5df6779](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5df6779b0820403c1a0fccf222defe58cb68d62e))
+* improve token extraction and .npmrc formatting for npm authentication ([9f18d51](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9f18d511949e1f2342cf41d6fb253d709a3fb6dd))
+* **release:** add main as regular branch to satisfy semantic-release validation ([beed14e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/beed14e663e2aa61a9e7755c99ea331422680877))
+* **release:** add semantic-release note to existing v1.0.0-beta.1 tag ([3d7d408](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3d7d408d2a5e63dfedf900686016ecd049f8b607))
+* **release:** correct dry-run next version and branch config ([767dc50](https://github.com/cloudinary-devs/create-cloudinary-react/commit/767dc506a1453737c6fd87fa8c60de0803fd7f7f))
+* **release:** ensure master branch exists for semantic-release validation ([3556b83](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3556b830a73234e2405332c05de7f7514dc0ed2a))
+* remove duplicate main branch config and improve tag fetching ([ebd8291](https://github.com/cloudinary-devs/create-cloudinary-react/commit/ebd82918fc4c0801d3ce31d8d516e1a77da6676c))
+* remove invalid tarballDir config and improve npm auth setup for semantic-release ([975a278](https://github.com/cloudinary-devs/create-cloudinary-react/commit/975a2785bbd5f03bda736669fdada7fec5599e9a))
+* simplify npm token extraction - use token even if whoami fails ([3d7700a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3d7700a0a29b921b3c881342369d266ff236f700))
+* simplify npm token extraction with multiple fallback methods ([dba9bb2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dba9bb2d1bf0357f95a5ce0f8e2318cb51da55fb))
+* update deployment beta ([3b60ec7](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3b60ec7366b174e35e809453b43da4777ed9b30a))
+* update Node.js version to 20 for semantic-release compatibility ([b1efc7c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b1efc7c4c6d6e5f1452489c34ab79b827ef7cb8f))
+* update release please ([00a932e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/00a932e17be240a983798e4cc4c0b4f628ff6699))
+* update semantic-release branch configuration with channel ([dcf9eab](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dcf9eaba7814e0e75bcaf9029508211282f5fb50))
+* use base version in manifest for release-please compatibility ([854e4f2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/854e4f258a5fdb5aed66e37d81ec1e07703f7d32))
+* use maintenance branch pattern to satisfy semantic-release validation ([e74ff2a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e74ff2aff29caf9b9812ee57d7cc1e9729c84e8a))
+
+
+### Features
+
+* initial release of create-cloudinary-react-vite ([865b5da](https://github.com/cloudinary-devs/create-cloudinary-react/commit/865b5da1ae437d9e47f69bb11f2ff778fc0ca16a))
+* use trusted publishing by disabling npm publish in semantic-release and publishing manually ([e2dbe19](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e2dbe197b45824843777ee26864d113ae7fadf27))
+
+# 1.0.0-beta.1 (2026-01-27)
+
+
+### Bug Fixes
+
+* add debug output and explicit npm config for semantic-release ([5cec039](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5cec03918105699758405ed8f9909220427be893))
+* add dummy release branch to satisfy semantic-release validation ([cce1e98](https://github.com/cloudinary-devs/create-cloudinary-react/commit/cce1e98c6bb0265630b5f1bf74b1ba0e30c30fc4))
+* add explicit branch checkout and debug info for semantic-release ([241ef66](https://github.com/cloudinary-devs/create-cloudinary-react/commit/241ef66baf8126019b58bfd42f2a19fe710d6c77))
+* add explicit registry URL to npm plugin config ([1fb7968](https://github.com/cloudinary-devs/create-cloudinary-react/commit/1fb7968a97acc0696be74c019a516b590b4e16ad))
+* add initial tag creation and maintenance branch pattern for semantic-release ([0aab3a3](https://github.com/cloudinary-devs/create-cloudinary-react/commit/0aab3a3788db401683284253cf4a63454cf5ce18))
+* check version change before publishing to npm ([c7cb003](https://github.com/cloudinary-devs/create-cloudinary-react/commit/c7cb0032c420c277245f7266830b219e906529c7))
+* configure git user before creating initial tag ([619fcc1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/619fcc1277846026070c03c9a09a16e1dd77fe0e))
+* configure main as both regular and prerelease branch, fetch all branches ([92475e1](https://github.com/cloudinary-devs/create-cloudinary-react/commit/92475e182873b75999d426f692961b34482283a2))
+* configure npm authentication for trusted publishing with semantic-release ([d2ad403](https://github.com/cloudinary-devs/create-cloudinary-react/commit/d2ad40360371fcdc28c7d97c4074dbc383ee7861))
+* extract npm token from .npmrc for semantic-release with trusted publishing ([a408d4c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/a408d4cca11c60d865aacf327ec28bf739de8cb3))
+* improve admin permission check in release workflow ([989b724](https://github.com/cloudinary-devs/create-cloudinary-react/commit/989b72458d5d48fee5c618ac97de84f85cd2c96d))
+* improve git branch setup for semantic-release detection ([86fd306](https://github.com/cloudinary-devs/create-cloudinary-react/commit/86fd306af6e1f6bea1312169dff96e9b5ce223f3))
+* improve npm token extraction with better debugging and fallbacks ([5c9ec06](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5c9ec064cdfc25dd3685fcb6e258e8739ddf1f9a))
+* improve npm token extraction with multiple locations and fallbacks ([5df6779](https://github.com/cloudinary-devs/create-cloudinary-react/commit/5df6779b0820403c1a0fccf222defe58cb68d62e))
+* improve token extraction and .npmrc formatting for npm authentication ([9f18d51](https://github.com/cloudinary-devs/create-cloudinary-react/commit/9f18d511949e1f2342cf41d6fb253d709a3fb6dd))
+* **release:** add main as regular branch to satisfy semantic-release validation ([beed14e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/beed14e663e2aa61a9e7755c99ea331422680877))
+* **release:** correct dry-run next version and branch config ([767dc50](https://github.com/cloudinary-devs/create-cloudinary-react/commit/767dc506a1453737c6fd87fa8c60de0803fd7f7f))
+* **release:** ensure master branch exists for semantic-release validation ([3556b83](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3556b830a73234e2405332c05de7f7514dc0ed2a))
+* remove duplicate main branch config and improve tag fetching ([ebd8291](https://github.com/cloudinary-devs/create-cloudinary-react/commit/ebd82918fc4c0801d3ce31d8d516e1a77da6676c))
+* remove invalid tarballDir config and improve npm auth setup for semantic-release ([975a278](https://github.com/cloudinary-devs/create-cloudinary-react/commit/975a2785bbd5f03bda736669fdada7fec5599e9a))
+* simplify npm token extraction - use token even if whoami fails ([3d7700a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3d7700a0a29b921b3c881342369d266ff236f700))
+* simplify npm token extraction with multiple fallback methods ([dba9bb2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dba9bb2d1bf0357f95a5ce0f8e2318cb51da55fb))
+* update deployment beta ([3b60ec7](https://github.com/cloudinary-devs/create-cloudinary-react/commit/3b60ec7366b174e35e809453b43da4777ed9b30a))
+* update Node.js version to 20 for semantic-release compatibility ([b1efc7c](https://github.com/cloudinary-devs/create-cloudinary-react/commit/b1efc7c4c6d6e5f1452489c34ab79b827ef7cb8f))
+* update release please ([00a932e](https://github.com/cloudinary-devs/create-cloudinary-react/commit/00a932e17be240a983798e4cc4c0b4f628ff6699))
+* update semantic-release branch configuration with channel ([dcf9eab](https://github.com/cloudinary-devs/create-cloudinary-react/commit/dcf9eaba7814e0e75bcaf9029508211282f5fb50))
+* use base version in manifest for release-please compatibility ([854e4f2](https://github.com/cloudinary-devs/create-cloudinary-react/commit/854e4f258a5fdb5aed66e37d81ec1e07703f7d32))
+* use maintenance branch pattern to satisfy semantic-release validation ([e74ff2a](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e74ff2aff29caf9b9812ee57d7cc1e9729c84e8a))
+
+
+### Features
+
+* initial release of create-cloudinary-react-vite ([865b5da](https://github.com/cloudinary-devs/create-cloudinary-react/commit/865b5da1ae437d9e47f69bb11f2ff778fc0ca16a))
+* use trusted publishing by disabling npm publish in semantic-release and publishing manually ([e2dbe19](https://github.com/cloudinary-devs/create-cloudinary-react/commit/e2dbe197b45824843777ee26864d113ae7fadf27))
+
# Changelog
## [1.0.0] - TBD
diff --git a/README.md b/README.md
index ba2e331..dcea541 100644
--- a/README.md
+++ b/README.md
@@ -1,65 +1,107 @@
# create-cloudinary-react
-> **Beta Release** - This is a beta version. We welcome feedback and bug reports!
-
-Part of the [Cloudinary Developers](https://github.com/cloudinary-devs) organization.
+[](https://www.npmjs.com/package/create-cloudinary-react)
+[](https://opensource.org/licenses/MIT)
+[](http://makeapullrequest.com)
-Scaffold a Cloudinary React + Vite + TypeScript project with interactive setup.
+**The fastest way to start building with Cloudinary and React.**
-## Prerequisites
+Scaffold a modern, production-ready Cloudinary application with React 19, Vite 6, and TypeScript 5. Features interactive setup, automatic environment configuration, and built-in AI coding assistance.
- Node.js 18+ installed
- A Cloudinary account (free tier available)
- - [Sign up for free](https://cloudinary.com/users/register/free)
+ - [Sign up for free](https://cld.media/reactregister)
- Your cloud name is in your [dashboard](https://console.cloudinary.com/app/home/dashboard)
-## Usage
+> **Beta Release** - This is a beta version. We welcome feedback and bug reports!
+
+Part of the [Cloudinary Developers](https://github.com/cloudinary-devs) organization.
+
+
+
+## 📽️ Demo
+
+[](https://res.cloudinary.com/drir0kpia/video/upload/v1771449633/reactstarterdemo.mp4)
+
+
+## 🎬 Features
+
+- **🚀 Modern Stack**: React 19 + Vite 6 + TypeScript 5.7
+- **📦 Cloudinary SDKs**: Pre-configured `@cloudinary/react`
+- **🤖 AI-First**: Auto-generates configuration for Cursor, GitHub Copilot, and Claude
+- **🛠️ Best Practices**: ESLint 9 + TypeScript-ESLint, strict type checking
+- **⚡ Interactive Setup**: Validates your cloud name and configures `.env` automatically
+- **🎨 Typed Components**: Includes a fully typed Upload Widget component
+- **🔌 MCP Support**: Built-in Model Context Protocol configuration for advanced AI integrations
+
+## 🚀 Quick Start
+
+Ensure you have Node.js 18+ installed.
```bash
npx create-cloudinary-react
```
+*(No installation required)*
+
+The CLI will guide you through:
+1. **Project Name**: naming your new folder
+2. **Cloud Name**: entering your [Cloudinary cloud name](https://console.cloudinary.com/app/home/dashboard)
+3. **Upload Preset** (Optional): handling unsigned uploads
+4. **AI Assistant**: generating custom rules for your tool of choice (Cursor, VS Code, etc.)
+
+## 🛠️ What's Included
-The CLI will prompt you for:
-- Project name
-- **Cloudinary cloud name** (found in your [dashboard](https://console.cloudinary.com/app/home/dashboard))
-- Upload preset (optional - required for uploads, but transformations work without it)
-- AI coding assistant(s) you're using (Cursor, GitHub Copilot, Claude, etc.)
-- Whether to install dependencies
-- Whether to start dev server
+Your new project comes with:
-## Features
+- **`src/`**: specialized for Cloudinary workflows
+- **`src/components/UploadWidget.tsx`**: A ready-to-use, typed upload component
+- **`.env`**: Pre-filled with your Cloud Name (and Upload Preset if provided)
+- **`README.md`**: Custom instructions for your specific project
+- **AI Configuration**:
+ - `.cursorrules` / `.cursor/mcp.json` (for Cursor)
+ - `.github/copilot-instructions.md` (for Copilot)
+ - `.claude` / `claude.md` (for Claude)
-- ✅ Interactive setup with validation
-- ✅ Pre-configured Cloudinary React SDK
-- ✅ TypeScript + Vite + React 19
-- ✅ Typed Upload Widget component
-- ✅ Environment variables with VITE_ prefix
-- ✅ Multi-tool AI assistant support (Cursor, GitHub Copilot, Claude, and more)
-- ✅ MCP configuration for Cloudinary integration
-- ✅ ESLint + TypeScript configured
+## 🤖 AI Assistant Support
-## AI Assistant Support
+We believe AI is the future of development. This starter kit doesn't just give you code; it gives your AI context.
-During setup, you'll be asked which AI coding assistant(s) you're using. The CLI will generate the appropriate configuration files:
+During setup, select your AI tool to generate **Context Rules**. These rules teach your AI:
+- How to construct Cloudinary transformation URLs correctly
+- How to use the `@cloudinary/react` SDK components
+- Common pitfalls to avoid (like mixing up import paths)
+- How to handle upload widget events
-- ✅ **Cursor** → `.cursorrules` + `.cursor/mcp.json` (if selected)
-- ✅ **GitHub Copilot** → `.github/copilot-instructions.md`
-- ✅ **Claude Code / Claude Desktop** → `.claude`, `claude.md` + `.cursor/mcp.json` (if selected)
-- ✅ **Generic AI tools** → `AI_INSTRUCTIONS.md`, `PROMPT.md`
+**Supported Tools:**
+- ✅ **Cursor** (Rules + MCP)
+- ✅ **GitHub Copilot** (Instructions)
+- ✅ **Claude** (Project context + MCP)
+- ✅ **Generic LLMs** (System prompts provided)
-**MCP Configuration**: The `.cursor/mcp.json` file is automatically generated if you select Cursor or Claude, as it works with both tools.
+## 📋 Prerequisites
-These rules help AI assistants understand Cloudinary React SDK patterns, common errors, and best practices. The generated app also includes an "AI Prompts" section with ready-to-use suggestions for your AI assistant.
+- **Node.js 18+**
+- **Cloudinary Account**: [Sign up for free](https://cloudinary.com/users/register/free) if you haven't already.
-## Development
+## 🤝 Contributing
+
+Contributions are welcome! Please feel free to submit a Pull Request.
+
+1. Fork the repository
+2. Create your feature branch (`git checkout -b feature/amazing-feature`)
+3. Commit your changes (`git commit -m 'feat: add some amazing feature'`)
+4. Push to the branch (`git push origin feature/amazing-feature`)
+5. Open a Pull Request
+
+## ⚙️ Development
This project uses [Conventional Commits](https://www.conventionalcommits.org/) for version management and [semantic-release](https://github.com/semantic-release/semantic-release) for automated releases.
### Release Process
-Releases are triggered manually via GitHub Actions workflow. The workflow uses npm trusted publishing (OIDC) for secure package publishing.
+Releases are triggered manually via GitHub Actions workflow. The workflow uses npm trusted publishing (OIDC) for secure package publishing. New versions are published to npm when the workflow runs without dry run.
-**Dry run (default):** When you run the workflow, "Dry run only" is checked by default. This runs semantic-release in dry-run mode—**no git push, no tags, no npm publish**. Use this to verify the next version and release notes before doing a real release. To publish for real, run the workflow again and **uncheck** "Dry run only".
+**Dry run (default):** When you run the workflow, "Dry run only" is checked by default. This runs semantic-release in dry-run mode—**no git push, no tags, no npm publish**. Use this to verify the next version and release notes before doing a real release. To publish for real, run the workflow again and **uncheck** "Dry run only". Each real release creates a GitHub release, updates CHANGELOG, and publishes the new version to npm (when the version changes).
### Commit Format
@@ -79,3 +121,6 @@ Releases are triggered manually via GitHub Actions workflow. The workflow uses n
- `perf`: Performance improvements
- `chore`: Other changes
+## 📄 License
+
+This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
diff --git a/cli.js b/cli.js
index 5965bc4..01a6c15 100755
--- a/cli.js
+++ b/cli.js
@@ -7,6 +7,7 @@ import { execSync } from 'child_process';
import inquirer from 'inquirer';
import chalk from 'chalk';
import fs from 'fs-extra';
+import { parseArgs } from 'node:util';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
@@ -24,97 +25,144 @@ function isValidProjectName(name) {
}
async function main() {
- console.log(chalk.cyan.bold('\n🚀 Cloudinary React + Vite Boilerplate\n'));
- console.log(chalk.gray('💡 Need a Cloudinary account? Sign up for free: https://cloudinary.com/users/register/free\n'));
+
+ let answers = {};
+
+ if (process.argv.includes('--headless')) {
- const answers = await inquirer.prompt([
- {
- type: 'input',
- name: 'projectName',
- message: 'Project name:',
- default: 'my-cloudinary-app',
- validate: (input) => {
- if (!input.trim()) {
- return 'Project name cannot be empty';
+ const { values, positionals } = parseArgs({
+ options: {
+ headless: {
+ type: 'boolean'
+ },
+ projectName: {
+ type: 'string',
+ default: 'my-cloudinary-app'
+ },
+ cloudName: {
+ type: 'string'
+ },
+ hasUploadPreset: {
+ type: 'boolean',
+ default: false
+ },
+ uploadPreset: {
+ type: 'string'
+ },
+ aiTools: {
+ type: 'string',
+ multiple: true,
+ default: ['cursor']
+ },
+ installDeps: {
+ type: 'boolean',
+ default: true
+ },
+ startDev: {
+ type: 'boolean',
+ default: false
}
- if (!isValidProjectName(input)) {
- return 'Project name can only contain letters, numbers, hyphens, and underscores';
- }
- if (existsSync(input)) {
- return `Directory "${input}" already exists. Please choose a different name.`;
- }
- return true;
+ }
+ });
+
+ Object.assign(answers, values);
+
+ } else {
+
+ console.log(chalk.cyan.bold('\n🚀 Cloudinary React Starter Kit\n'));
+ console.log(chalk.gray('💡 Need a Cloudinary account? Sign up for free: https://cld.media/reactregister\n'));
+
+ const questions = [
+ {
+ type: 'input',
+ name: 'projectName',
+ message: 'What’s your project’s name?\n',
+ default: 'my-cloudinary-app',
+ validate: (input) => {
+ if (!input.trim()) {
+ return 'Project name cannot be empty';
+ }
+ if (!isValidProjectName(input)) {
+ return 'Project name can only contain letters, numbers, hyphens, and underscores';
+ }
+ if (existsSync(input)) {
+ return `Directory "${input}" already exists. Please choose a different name.`;
+ }
+ return true;
+ },
},
- },
- {
- type: 'input',
- name: 'cloudName',
- message: 'Cloudinary Cloud Name:',
- description: chalk.gray(
- 'Your cloud name is shown in your dashboard: https://console.cloudinary.com/app/home/dashboard'
- ),
- validate: (input) => {
- if (!input.trim()) {
- return chalk.yellow(
- 'Cloud name is required.\n' +
- ' → Sign up: https://cloudinary.com/users/register/free\n' +
- ' → Find your cloud name: https://console.cloudinary.com/app/home/dashboard'
- );
- }
- if (!isValidCloudName(input)) {
- return 'Cloud name can only contain lowercase letters, numbers, hyphens, and underscores';
- }
- return true;
+ {
+ type: 'input',
+ name: 'cloudName',
+ message:
+ 'What’s your Cloudinary cloud name?\n' +
+ chalk.gray(' → Find your cloud name: https://console.cloudinary.com/app/home/dashboard') + '\n',
+ validate: (input) => {
+ if (!input.trim()) {
+ return chalk.yellow(
+ 'Cloud name is required.\n' +
+ ' → Sign up: https://cld.media/reactregister\n' +
+ ' → Find your cloud name: https://console.cloudinary.com/app/home/dashboard'
+ );
+ }
+ if (!isValidCloudName(input)) {
+ return 'Cloud name can only contain lowercase letters, numbers, hyphens, and underscores';
+ }
+ return true;
+ },
},
- },
- {
- type: 'confirm',
- name: 'hasUploadPreset',
- message: 'Do you have an unsigned upload preset? (Required for uploads, optional for transformations)',
- default: false,
- description: chalk.gray(
- 'Upload presets enable client-side uploads. You can set one up later at:\n' +
- 'https://console.cloudinary.com/app/settings/upload/presets'
- ),
- },
- {
- type: 'input',
- name: 'uploadPreset',
- message: 'Upload Preset Name:',
- when: (answers) => answers.hasUploadPreset,
- validate: (input) => {
- if (!input.trim()) {
- return 'Upload preset name cannot be empty';
- }
- return true;
+ {
+ type: 'confirm',
+ name: 'hasUploadPreset',
+ message:
+ 'Do you have an unsigned upload preset?\n' +
+ chalk.gray(' → You’ll need one if you want to upload new images to Cloudinary,\n but not if you only want to transform or deliver existing images.') + '\n' +
+ chalk.gray(' → Create one here: https://console.cloudinary.com/app/settings/upload/presets') + '\n',
+ default: false,
},
- },
- {
- type: 'checkbox',
- name: 'aiTools',
- message: 'Which AI coding assistant(s) are you using? (Select all that apply)',
- choices: [
- { name: 'Cursor', value: 'cursor' },
- { name: 'GitHub Copilot', value: 'copilot' },
- { name: 'Claude Code / Claude Desktop', value: 'claude' },
- { name: 'Other / Generic AI tools', value: 'generic' },
- ],
- default: ['cursor'],
- },
- {
- type: 'confirm',
- name: 'installDeps',
- message: 'Install dependencies now?',
- default: true,
- },
- {
- type: 'confirm',
- name: 'startDev',
- message: 'Start development server?',
- default: false,
- when: (answers) => answers.installDeps,
- },
- ]);
+ {
+ type: 'input',
+ name: 'uploadPreset',
+ message: 'What’s your unsigned upload preset’s name?\n',
+ when: (answers) => answers.hasUploadPreset,
+ validate: (input) => {
+ if (!input.trim()) {
+ return 'Upload preset name cannot be empty';
+ }
+ return true;
+ },
+ },
+ {
+ type: 'checkbox',
+ name: 'aiTools',
+ message:
+ 'Which AI coding assistant(s) are you using? (Select all that apply)\n' +
+ chalk.gray(' We’ll add local instruction files so your assistant knows Cloudinary patterns.\n'),
+ choices: [
+ { name: 'Cursor', value: 'cursor' },
+ { name: 'GitHub Copilot', value: 'copilot' },
+ { name: 'Claude Code', value: 'claude' },
+ { name: 'Other / Generic AI tools', value: 'generic' },
+ ],
+ default: ['cursor'],
+ },
+ {
+ type: 'confirm',
+ name: 'installDeps',
+ message: 'Install dependencies now?\n',
+ default: true,
+ },
+ {
+ type: 'confirm',
+ name: 'startDev',
+ message: 'Start development server?\n',
+ default: false,
+ when: (answers) => answers.installDeps,
+ },
+ ];
+
+ answers = await inquirer.prompt(questions);
+ }
const { projectName, cloudName, uploadPreset, aiTools, installDeps, startDev } = answers;
@@ -139,6 +187,9 @@ async function main() {
PROJECT_NAME: projectName,
CLOUD_NAME: cloudName,
UPLOAD_PRESET: uploadPreset || '',
+ UPLOAD_PRESET_ENV_LINE: uploadPreset
+ ? `- \`VITE_CLOUDINARY_UPLOAD_PRESET\`: ${uploadPreset}`
+ : '- `VITE_CLOUDINARY_UPLOAD_PRESET`: (not set - add one for uploads)',
};
// Function to copy template file
@@ -203,8 +254,7 @@ async function main() {
}
if (aiTools.includes('claude')) {
- writeFileSync(join(projectPath, '.claude'), aiRulesContent);
- writeFileSync(join(projectPath, 'claude.md'), aiRulesContent);
+ writeFileSync(join(projectPath, 'CLAUDE.md'), aiRulesContent);
}
if (aiTools.includes('generic')) {
@@ -212,18 +262,21 @@ async function main() {
writeFileSync(join(projectPath, 'PROMPT.md'), aiRulesContent);
}
- // Generate MCP configuration if using Cursor or Claude (MCP works with both)
- if (aiTools.includes('cursor') || aiTools.includes('claude')) {
- const mcpTemplatePath = join(TEMPLATES_DIR, '.cursor/mcp.json.template');
- if (existsSync(mcpTemplatePath)) {
+ // Generate MCP configuration: Cursor uses .cursor/mcp.json, Claude Code uses .mcp.json in project root
+ const mcpTemplatePath = join(TEMPLATES_DIR, '.cursor/mcp.json.template');
+ if (existsSync(mcpTemplatePath)) {
+ const mcpContent = replaceTemplate(
+ readFileSync(mcpTemplatePath, 'utf-8'),
+ templateVars
+ );
+ if (aiTools.includes('cursor')) {
const cursorDir = join(projectPath, '.cursor');
mkdirSync(cursorDir, { recursive: true });
- const mcpContent = replaceTemplate(
- readFileSync(mcpTemplatePath, 'utf-8'),
- templateVars
- );
writeFileSync(join(cursorDir, 'mcp.json'), mcpContent);
}
+ if (aiTools.includes('claude')) {
+ writeFileSync(join(projectPath, '.mcp.json'), mcpContent);
+ }
}
}
@@ -235,6 +288,17 @@ async function main() {
console.log(chalk.green('✅ Project created successfully!\n'));
+ if (aiTools && aiTools.length > 0) {
+ console.log(chalk.cyan('📋 AI assistant files created:'));
+ if (aiTools.includes('cursor')) console.log(chalk.gray(' • Cursor: .cursorrules'));
+ if (aiTools.includes('copilot')) console.log(chalk.gray(' • GitHub Copilot: .github/copilot-instructions.md'));
+ if (aiTools.includes('claude')) console.log(chalk.gray(' • Claude: CLAUDE.md'));
+ if (aiTools.includes('generic')) console.log(chalk.gray(' • Generic: AI_INSTRUCTIONS.md, PROMPT.md'));
+ if (aiTools.includes('cursor')) console.log(chalk.gray(' • MCP (Cursor): .cursor/mcp.json'));
+ if (aiTools.includes('claude')) console.log(chalk.gray(' • MCP (Claude Code): .mcp.json'));
+ console.log('');
+ }
+
if (!answers.hasUploadPreset) {
console.log(chalk.yellow('\n📝 Note: Upload preset not configured'));
console.log(chalk.gray(' • Transformations will work with sample images'));
@@ -243,7 +307,8 @@ async function main() {
console.log(chalk.cyan(' 1. Go to https://console.cloudinary.com/app/settings/upload/presets'));
console.log(chalk.cyan(' 2. Click "Add upload preset"'));
console.log(chalk.cyan(' 3. Set it to "Unsigned" mode'));
- console.log(chalk.cyan(' 4. Add the preset name to your .env file\n'));
+ console.log(chalk.cyan(' 4. Add the preset name to your .env file'));
+ console.log(chalk.cyan(' 5. Save the file and restart the dev server so it loads correctly\n'));
}
if (installDeps) {
diff --git a/package-lock.json b/package-lock.json
index 3ce467b..c8d64fc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "create-cloudinary-react",
- "version": "1.0.0-beta.1",
+ "version": "1.0.0-beta.15",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "create-cloudinary-react",
- "version": "1.0.0-beta.1",
+ "version": "1.0.0-beta.15",
"license": "MIT",
"dependencies": {
"chalk": "^5.3.0",
diff --git a/package.json b/package.json
index 8705924..6718323 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "create-cloudinary-react",
- "version": "1.0.0-beta.1",
+ "version": "1.0.0-beta.21",
"description": "Scaffold a Cloudinary React + Vite + TypeScript project with interactive setup",
"type": "module",
"bin": {
@@ -11,7 +11,6 @@
"react",
"vite",
"typescript",
- "boilerplate",
"scaffold"
],
"author": "",
diff --git a/templates/.cursorrules.template b/templates/.cursorrules.template
index 51779ff..c95f311 100644
--- a/templates/.cursorrules.template
+++ b/templates/.cursorrules.template
@@ -1,52 +1,175 @@
# Cloudinary React SDK Patterns & Common Errors
+**Scope**: These rules apply to **React (web)** with the browser Upload Widget. The **default** is **Vite** (create-cloudinary-react uses Vite). They also work with **other bundlers** (Create React App, Next.js, Parcel, etc.): only **how you read env vars** changes; see **"Other bundlers (non-Vite)"** below. Rules-only users: see **"Project setup (rules-only / without CLI)"** for the reusable Cloudinary instance, env, Upload Widget (unsigned/signed), and video player. For **React Native** uploads (including signed upload), see: https://cloudinary.com/documentation/react_native_image_and_video_upload#signed_upload — same “never expose secret, generate signature on backend” principle, but React Native uses the `upload()` method and backend SDKs differently.
+
## Official Documentation
- **Transformation Rules**: https://cloudinary.com/documentation/cloudinary_transformation_rules.md
-- **Transformation Reference**: https://cloudinary.com/documentation/transformation_reference
-- **React Image Transformations & Plugins**: https://cloudinary.com/documentation/react_image_transformations#plugins
-- **React Video Transformations**: https://cloudinary.com/documentation/react_video_transformations
-- **Cloudinary Video Player** (standalone player): https://cloudinary.com/documentation/cloudinary_video_player
-- **Video Player React Tutorial**: https://cloudinary.com/documentation/video_player_react_tutorial#banner
+- **Transformation Reference**: https://cloudinary.com/documentation/transformation_reference.md
+- **React Image Transformations & Plugins**: https://cloudinary.com/documentation/react_image_transformations.md#plugins
+- **React Video Transformations**: https://cloudinary.com/documentation/react_video_transformations.md
+- **Cloudinary Video Player** (standalone player): https://cloudinary.com/documentation/cloudinary_video_player.md
+- **Video Player React Tutorial**: https://cloudinary.com/documentation/video_player_react_tutorial.md
+- **Upload Widget (signed uploads)**: https://cloudinary.com/documentation/upload_widget.md#signed_uploads
+- **Upload assets in Next.js (backend signature)**: https://cloudinary.com/documentation/upload_assets_in_nextjs_tutorial.md
+- **Cloudinary Node.js SDK (server-side signing)** — use **v2**: `import { v2 as cloudinary } from 'cloudinary'`; do not use v1 (e.g. 1.47.0). https://cloudinary.com/documentation/node_integration.md
+- **React Native image and video upload (signed)**: https://cloudinary.com/documentation/react_native_image_and_video_upload.md#signed_upload
- Always consult the official transformation rules when creating transformations
- Use only officially supported parameters from the transformation reference
+**Golden rule for @cloudinary/url-gen:** When generating overlay or transformation code, use **only** the "Import reference" table and "Canonical overlay block" in these rules. Copy those import paths and usage patterns exactly; do not guess or derive paths (e.g. `text` and `image` are from `qualifiers/source`, not `actions/overlay`; `Position` is from `qualifiers/position`, not `positioning/Position`).
+
---
# 📋 PATTERNS (How to Do It Right)
+## Project setup (rules-only / without CLI)
+
+If the user is **not** using the create-cloudinary-react CLI and only has these rules, generate the following so they get correct config, env, and widget setup.
+
+**1. Environment (.env)**
+Create a `.env` file in the project root with **Vite prefix** (required for client access):
+- `VITE_CLOUDINARY_CLOUD_NAME=your_cloud_name` (required)
+- `VITE_CLOUDINARY_UPLOAD_PRESET=your_unsigned_preset_name` (optional; required for unsigned upload widget)
+- Save the `.env` file after editing it, then restart the dev server so changes load correctly. Use `import.meta.env.VITE_*` in code, not `process.env`.
+
+**2. Reusable Cloudinary instance (config)**
+Create a config file (e.g. `src/cloudinary/config.ts`) so the rest of the app can use a single `cld` instance:
+```ts
+import { Cloudinary } from '@cloudinary/url-gen';
+
+const cloudName = import.meta.env.VITE_CLOUDINARY_CLOUD_NAME;
+if (!cloudName) {
+ throw new Error('VITE_CLOUDINARY_CLOUD_NAME is not set. Add it to .env with the VITE_ prefix.');
+}
+
+export const cld = new Cloudinary({ cloud: { cloudName } });
+export const uploadPreset = import.meta.env.VITE_CLOUDINARY_UPLOAD_PRESET || '';
+```
+- Use **this** pattern for the reusable instance. Everywhere else: `import { cld } from './cloudinary/config'` (or the path the user chose) and call `cld.image(publicId)` / `cld.video(publicId)`.
+
+**3. Upload Widget (unsigned, from scratch)**
+
+**Strict pattern (always follow this exactly):**
+1. **Script in `index.html`** (required): Add `` to `index.html`. Do **not** rely only on dynamic script injection from React — it's fragile.
+2. **Poll in useEffect** (required): In `useEffect`, poll with `setInterval` (e.g. every 100ms) until `typeof window.cloudinary?.createUploadWidget === 'function'`. Only then create the widget. A single check (even in `onload`) is **not** reliable because `window.cloudinary` can exist before `createUploadWidget` is attached.
+3. **Add a timeout**: Set a timeout (e.g. 10 seconds) to stop polling and show an error if the script never loads. Clear both interval and timeout in cleanup.
+4. **Create widget once**: When `createUploadWidget` is available, create the widget and store it in a **ref**. Clear the interval and timeout. Pass options: `{ cloudName, uploadPreset, sources: ['local', 'camera', 'url'], multiple: false }`.
+5. **Open on click**: Attach a click listener to a button that calls `widgetRef.current?.open()`. Remove the listener in useEffect cleanup.
+
+❌ **Do NOT**: Check only `window.cloudinary` (not enough); do a single check in `onload` (unreliable); skip the script in `index.html`; poll forever without a timeout.
+- **Signed uploads**: Do not use only `uploadPreset`; use the pattern under "Secure (Signed) Uploads" (uploadSignature as function, fetch api_key, server includes upload_preset in signature).
+
+**4. Video player**
+- Use imperative video element only (create with document.createElement, append to container ref, pass to videoPlayer). See "Cloudinary Video Player (The Player)" for the full pattern.
+
+**5. Summary for rules-only users**
+- **Env**: Use your bundler's client env prefix and access (Vite: `VITE_` + `import.meta.env.VITE_*`; see "Other bundlers" if not Vite).
+- **Reusable instance**: One config file that creates and exports `cld` (and optionally `uploadPreset`) from `@cloudinary/url-gen`; use it everywhere.
+- **Upload widget**: Script in index.html (required); in useEffect, **poll** until `createUploadWidget` is a function, then create widget once and store in ref; unsigned = cloudName + uploadPreset; signed = use uploadSignature function and backend.
+- **Video player**: Imperative video element (createElement, append to container ref, pass to videoPlayer); dispose + removeChild in cleanup; fall back to AdvancedVideo if init fails.
+
+**If the user is not using Vite:** Use their bundler's client env prefix and access in the config file and everywhere you read env. Examples: Create React App → `REACT_APP_CLOUDINARY_CLOUD_NAME`, `process.env.REACT_APP_CLOUDINARY_CLOUD_NAME`; Next.js (client) → `NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME`, `process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME`. The rest (cld instance, widget options, video player) is the same.
+
## Environment Variables
-- **Vite requires VITE_ prefix** - Environment variables MUST start with `VITE_` to be exposed to the browser
-- ✅ CORRECT: `VITE_CLOUDINARY_CLOUD_NAME=mycloud` in `.env` file
-- ✅ CORRECT: `import.meta.env.VITE_CLOUDINARY_CLOUD_NAME` (not `process.env`)
-- Always restart dev server after adding/updating `.env` variables
+- **Default: Vite** — Vite requires `VITE_` prefix; use `import.meta.env.VITE_CLOUDINARY_CLOUD_NAME` (not `process.env`). Restart dev server after changing `.env`.
+- ✅ CORRECT (Vite): `VITE_CLOUDINARY_CLOUD_NAME=mycloud` in `.env`; `import.meta.env.VITE_CLOUDINARY_CLOUD_NAME`
+
+## Other bundlers (non-Vite)
+- **Only the env access changes.** All other patterns (reusable `cld`, Upload Widget, Video Player, overlays, signed uploads) are bundler-agnostic.
+- **Create React App**: Prefix `REACT_APP_`; access `process.env.REACT_APP_CLOUDINARY_CLOUD_NAME`, `process.env.REACT_APP_CLOUDINARY_UPLOAD_PRESET`. Restart dev server after `.env` changes.
+- **Next.js (client)**: Prefix `NEXT_PUBLIC_` for client; access `process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME`, etc. Server-side can use `process.env.CLOUDINARY_*` without `NEXT_PUBLIC_`.
+- **Parcel / other**: Check the bundler's docs for "exposing environment variables to the client" (often a prefix or allowlist). Use that prefix and the documented access (e.g. `process.env.*`).
+- **Config file**: In `src/cloudinary/config.ts` (or equivalent), read cloud name and upload preset using the **user's bundler** env API (e.g. for CRA: `process.env.REACT_APP_CLOUDINARY_CLOUD_NAME`). Same `new Cloudinary({ cloud: { cloudName } })` and exports; only the env read line changes.
## Upload Presets
-- **Unsigned upload presets are required for client-side uploads** - Transformations work without them, but uploads need an unsigned preset
-- ✅ Create unsigned upload preset: https://console.cloudinary.com/app/settings/upload/presets
+- **Unsigned** = client-only uploads (no backend). **Signed** = backend required, more secure. See **"Signed vs unsigned uploads"** below for when to use which.
+- ✅ Create unsigned upload preset (for simple client uploads): https://console.cloudinary.com/app/settings/upload/presets
- ✅ Set preset in `.env`: `VITE_CLOUDINARY_UPLOAD_PRESET=your-preset-name`
- ✅ Use in code: `import { uploadPreset } from './cloudinary/config'`
- ⚠️ If upload preset is missing, the Upload Widget will show an error message
- ⚠️ Upload presets must be set to "Unsigned" mode for client-side usage (no API key/secret needed)
+- **When unsigned upload fails**: First check that the user configured their upload preset:
+ 1. Is `VITE_CLOUDINARY_UPLOAD_PRESET` set in `.env`? (must match preset name exactly)
+ 2. Does the preset exist in the dashboard under Settings → Upload → Upload presets?
+ 3. Is the preset set to **Unsigned** (not Signed)?
+ 4. Was the dev server restarted after adding/updating `.env`?
+
+## Installing Cloudinary packages
+- ✅ **Install the latest**: When adding Cloudinary packages, use `npm install ` **with no version** so npm installs the latest compatible version (e.g. `npm install cloudinary-video-player`). In package.json use a **caret range** (e.g. `"cloudinary-video-player": "^1.0.0"`) so future installs get the latest compatible. Do not pin to an exact version unless you have verified it exists on npm.
+- ✅ **Package names only**: Use **only** these names: `@cloudinary/react`, `@cloudinary/url-gen`, `cloudinary-video-player` (standalone player), `cloudinary` (Node server-side only). Do not invent names (e.g. no `@cloudinary/video-player`).
+- ❌ **WRONG**: `npm install cloudinary-video-player@1.2.3` or `"cloudinary-video-player": "1.2.3"` (exact pin) — versions may not exist and break installs.
+- ✅ **Correct**: `npm install cloudinary-video-player` (no version) or in package.json: `"cloudinary-video-player": "^1.0.0"` (caret = latest compatible).
## Import Patterns
- ✅ Import Cloudinary instance: `import { cld } from './cloudinary/config'`
- ✅ Import components: `import { AdvancedImage, AdvancedVideo } from '@cloudinary/react'`
-- ✅ Import transformations: `import { fill } from '@cloudinary/url-gen/actions/resize'`
-- ✅ Import effects: `import { blur } from '@cloudinary/url-gen/actions/effect'`
-- ✅ Import delivery: `import { format, quality } from '@cloudinary/url-gen/actions/delivery'`
-- ✅ Import qualifiers: `import { auto } from '@cloudinary/url-gen/qualifiers/format'`
-- ✅ Import qualifiers: `import { auto as autoQuality } from '@cloudinary/url-gen/qualifiers/quality'`
- ✅ Import plugins: `import { responsive, lazyload, placeholder } from '@cloudinary/react'`
+- ✅ **For transformations and overlays**, use **only** the exact paths in "Import reference: @cloudinary/url-gen" and the "Canonical overlay block" below. Do **not** guess subpaths (e.g. `text` and `image` are from `qualifiers/source`, not `actions/overlay`).
+
+## Import reference: @cloudinary/url-gen (use these exact paths only)
+
+**Rule:** Do not invent or guess import paths for `@cloudinary/url-gen`. Use **only** the paths in the table and canonical block below. Copy the import statements exactly; do not derive paths (e.g. `@cloudinary/url-gen/overlay` exports only `source` — `text` and `image` are from **`qualifiers/source`**; `Position` is from **`qualifiers/position`**, not `positioning/Position`). Wrong paths cause "module not found" or "does not exist".
+
+| Purpose | Exact import |
+|--------|----------------|
+| Cloudinary instance (config) | `import { Cloudinary } from '@cloudinary/url-gen';` |
+| Resize (fill) | `import { fill } from '@cloudinary/url-gen/actions/resize';` |
+| Resize (scale, for overlays) | `import { scale } from '@cloudinary/url-gen/actions/resize';` |
+| Delivery format/quality | `import { format, quality } from '@cloudinary/url-gen/actions/delivery';` |
+| Format qualifier (auto) | `import { auto } from '@cloudinary/url-gen/qualifiers/format';` |
+| Quality qualifier (auto) | `import { auto as autoQuality } from '@cloudinary/url-gen/qualifiers/quality';` |
+| Effects (e.g. blur) | `import { blur } from '@cloudinary/url-gen/actions/effect';` |
+| Overlay source | `import { source } from '@cloudinary/url-gen/actions/overlay';` |
+| Overlay text / image (source types) | `import { text, image } from '@cloudinary/url-gen/qualifiers/source';` |
+| Overlay image transformation | `import { Transformation } from '@cloudinary/url-gen/transformation/Transformation';` |
+| Position (overlay) | `import { Position } from '@cloudinary/url-gen/qualifiers/position';` |
+| Gravity/compass | `import { compass } from '@cloudinary/url-gen/qualifiers/gravity';` |
+| Text style (overlay) | `import { TextStyle } from '@cloudinary/url-gen/qualifiers/textStyle';` |
+| Types | `import type { CloudinaryImage, CloudinaryVideo } from '@cloudinary/url-gen';` |
+
+**Canonical overlay block (copy these imports and patterns exactly):**
+```ts
+// Overlay imports — text/image from qualifiers/source, NOT actions/overlay
+import { source } from '@cloudinary/url-gen/actions/overlay';
+import { text, image } from '@cloudinary/url-gen/qualifiers/source';
+import { Position } from '@cloudinary/url-gen/qualifiers/position';
+import { TextStyle } from '@cloudinary/url-gen/qualifiers/textStyle';
+import { compass } from '@cloudinary/url-gen/qualifiers/gravity';
+import { Transformation } from '@cloudinary/url-gen/transformation/Transformation';
+import { scale } from '@cloudinary/url-gen/actions/resize';
+
+// Text overlay (compass with underscores: 'south_east', 'center')
+cld.image('id').overlay(
+ source(text('Hello', new TextStyle('Arial', 60).fontWeight('bold')).textColor('white'))
+ .position(new Position().gravity(compass('center')))
+);
+
+// Image overlay (logo/image with resize)
+cld.image('id').overlay(
+ source(image('logo').transformation(new Transformation().resize(scale().width(100))))
+ .position(new Position().gravity(compass('south_east')).offsetX(20).offsetY(20))
+);
+```
+
+- **Components** (AdvancedImage, AdvancedVideo, plugins) come from **`@cloudinary/react`**, not from `@cloudinary/url-gen`.
+- **Transformation actions and qualifiers** (resize, delivery, effect, overlay, etc.) come from **`@cloudinary/url-gen/actions/*`** and **`@cloudinary/url-gen/qualifiers/*`** with the exact subpaths above.
+- If an import fails, verify the package version (`@cloudinary/url-gen` in package.json) and the [Cloudinary URL-Gen SDK docs](https://cloudinary.com/documentation/sdks/js/url-gen/index.html) or [Transformation Builder reference](https://cloudinary.com/documentation/sdks/js/transformation_builder_reference).
## Creating Image & Video Instances
- ✅ Create image instance: `const img = cld.image(publicId)`
- ✅ Create video instance: `const video = cld.video(publicId)` (same pattern as images)
- ✅ Public ID format: Use forward slashes for folders (e.g., `'folder/subfolder/image'`)
- ✅ Public IDs are case-sensitive and should not include file extensions
+- ✅ **Sample assets**: Cloudinary may provide sample assets under `samples/`. **Assume they might not exist** (users can delete them); always handle load errors and provide fallbacks (see Image gallery). When they exist, use them for examples and demos instead of requiring uploads first.
+- ✅ **Sample public IDs that may be available** (use for galleries, demos; handle onError if missing):
+ - Images: `samples/cloudinary-icon`, `samples/two-ladies`, `samples/food/spices`, `samples/landscapes/beach-boat`, `samples/bike`, `samples/landscapes/girl-urban-view`, `samples/animals/reindeer`, `samples/food/pot-mussels`
+ - Video: `samples/elephants`
+- ✅ **Default / most reliable**: Start with `samples/cloudinary-icon` for a single image; use the list above for galleries or variety. Prefer uploaded assets when the user has them.
- ✅ Examples:
```tsx
const displayImage = cld.image('samples/cloudinary-icon');
const displayVideo = cld.video('samples/elephants');
+ // Gallery: e.g. ['samples/bike', 'samples/landscapes/beach-boat', 'samples/food/spices', ...]
```
## Transformation Patterns
@@ -74,30 +197,32 @@
### Transformation Best Practices
- ✅ Format and quality must use separate `.delivery()` calls
-- ✅ Always end with auto format/quality: `.delivery(format(auto())).delivery(quality(autoQuality()))`
+- ✅ Always end with auto format/quality: `.delivery(format(auto())).delivery(quality(autoQuality()))` unless user specifies a particular format or quality
- ✅ Use `gravity(auto())` unless user specifies a focal point
- ✅ Same transformation syntax works for both images and videos
## Plugin Patterns
+- ✅ **When the user asks for lazy loading or responsive images**: Use the **Cloudinary plugins** from `@cloudinary/react` — `responsive()`, `lazyload()`, `placeholder()` — with `AdvancedImage`. Do not use only native `loading="lazy"` or CSS-only responsive; the Cloudinary plugins handle breakpoints, lazy loading, and placeholders for Cloudinary URLs.
- ✅ Import plugins from `@cloudinary/react`
- ✅ Pass plugins as array: `plugins={[responsive(), lazyload(), placeholder()]}`
- ✅ Recommended plugin order:
- 1. `responsive()` - First
- 2. `lazyload()` - Second
- 3. `accessibility()` - Third
- 4. `placeholder()` - Last
+ 1. `responsive()` - First (handles breakpoints)
+ 2. `placeholder()` - Second (shows placeholder while loading)
+ 3. `lazyload()` - Third (delays loading until in viewport)
+ 4. `accessibility()` - Last (if needed)
- ✅ Always add `width` and `height` attributes to prevent layout shift
- ✅ Example:
```tsx
```
## Responsive Images Pattern
+- ✅ **Responsive images**: Use the Cloudinary `responsive()` plugin with `fill()` resize (not only CSS). **Lazy loading**: Use the Cloudinary `lazyload()` plugin with `AdvancedImage` (not only `loading="lazy"`).
- ✅ Use `responsive()` plugin with `fill()` resize
- ✅ Combine with `placeholder()` and `lazyload()` plugins
- ✅ Example:
@@ -111,12 +236,37 @@
/>
```
+## Image gallery with lazy loading and responsive
+- ✅ **When the user asks for an image gallery with lazy loading and responsive**: Use Cloudinary **plugins** with `AdvancedImage`: `responsive()`, `lazyload()`, `placeholder()` (see Plugin Patterns). Use `fill()` resize with the responsive plugin. Add `width` and `height` to prevent layout shift.
+- ✅ **Sample assets in galleries**: Use the sample public IDs from "Creating Image & Video Instances" (e.g. `samples/bike`, `samples/landscapes/beach-boat`, `samples/food/spices`, `samples/two-ladies`, `samples/landscapes/girl-urban-view`, `samples/animals/reindeer`, `samples/food/pot-mussels`, `samples/cloudinary-icon`). **Assume any sample might not exist** — users can delete them. Start with one reliable sample (e.g. `samples/cloudinary-icon`) or a short list; add **onError** handling and remove/hide failed images. Prefer **uploaded** assets when available (e.g. from UploadWidget) over samples.
+- ✅ **Handle load errors**: Use `onError` on `AdvancedImage` to hide or remove failed images (e.g. set state to filter out the publicId, or hide the parent). Provide user feedback (e.g. "Some images could not be loaded. Try uploading your own!") and upload functionality so users can add their own images.
+- ✅ **Fallback**: Default gallery list can be a subset of the sample list (e.g. `['samples/cloudinary-icon', 'samples/bike', 'samples/landscapes/beach-boat']`); when user uploads, append `result.public_id`. If an image fails to load, remove it from the list or hide it so the UI doesn't show broken images.
+
+## Image Overlays (text or logos)
+- ✅ **When the user asks for image overlays with text or logos**: Use `@cloudinary/url-gen` overlay APIs. Copy imports and patterns from the **"Import reference"** table and **"Canonical overlay block"** in these rules. Do not import `text` or `image` from `actions/overlay` — they are from **`qualifiers/source`**; only `source` is from `actions/overlay`.
+- ✅ **Import** `source` from `actions/overlay`; **`text` and `image` from `qualifiers/source`**. Also: `Position` from `qualifiers/position`, `TextStyle` from `qualifiers/textStyle`, `compass` from `qualifiers/gravity`, `Transformation` from `transformation/Transformation`, `scale` from `actions/resize`.
+- ✅ **compass()** takes **string** values, with **underscores**: `compass('center')`, `compass('south_east')`, `compass('north_west')`. ❌ WRONG: `compass(southEast)` or `'southEast'` (no camelCase).
+- ✅ **Overlay image**: Use `new Transformation()` **inside** `.transformation()`: `image('logo').transformation(new Transformation().resize(scale().width(100)))`. ❌ WRONG: `image('logo').transformation().resize(...)` (`.transformation()` does not return a chainable object).
+- ✅ **Text overlay**: `fontWeight` goes on **TextStyle**: `new TextStyle('Arial', 60).fontWeight('bold')`. `textColor` goes on the **text source** (chained after `text(...)`): `text('Hello', new TextStyle('Arial', 60)).textColor('white')`.
+- ✅ **Position** is chained **after** `source(...)`, not inside: `source(image('logo').transformation(...)).position(new Position().gravity(compass('south_east')).offsetX(20).offsetY(20))`.
+- ✅ **Image overlay pattern**: `baseImage.overlay(source(image('id').transformation(new Transformation().resize(scale().width(100)))).position(new Position().gravity(compass('south_east')).offsetX(20).offsetY(20)))`. (Import `scale` from `@cloudinary/url-gen/actions/resize` if needed.)
+- ✅ **Text overlay pattern**: `baseImage.overlay(source(text('Your Text', new TextStyle('Arial', 60).fontWeight('bold')).textColor('white')).position(new Position().gravity(compass('center'))))`.
+- ✅ Docs: React Image Transformations and transformation reference for overlay syntax.
+
## Upload Widget Pattern
- ✅ Use component: `import { UploadWidget } from './cloudinary/UploadWidget'`
-- ✅ Load script in `index.html`:
+
+**Strict initialization pattern (always follow this exactly):**
+1. ✅ **Script in `index.html`** (required):
```html
```
+2. ✅ **Poll in useEffect until `createUploadWidget` is available** (required): Use `setInterval` (e.g. every 100ms) to check `typeof window.cloudinary?.createUploadWidget === 'function'`. Only create the widget when this returns `true`. Clear the interval once ready.
+3. ✅ **Add a timeout** (e.g. 10 seconds) to stop polling and show an error state if the script never loads. Clear both interval and timeout in cleanup and when ready.
+4. ✅ **Create widget once**, store in a ref. Cleanup: clear interval, clear timeout, remove click listener.
+
+❌ **Do NOT**: Check only `window.cloudinary` (the function may not be attached yet); do a single check in `onload` (unreliable timing); skip `index.html` and rely only on dynamic injection; poll forever without a timeout.
+
- ✅ Create unsigned upload preset in dashboard at `settings/upload/presets`
- ✅ Add to `.env`: `VITE_CLOUDINARY_UPLOAD_PRESET=your_preset_name`
- ✅ Handle callbacks:
@@ -132,24 +282,116 @@
```
- ✅ Upload result contains: `public_id`, `secure_url`, `width`, `height`, etc.
+## Signed vs unsigned uploads (when to use which)
+
+**Unsigned uploads** (simpler, no backend required):
+- Use when: Quick prototypes, low-risk apps, or when anyone with the preset name may upload.
+- Preset: Create an **Unsigned** upload preset in Cloudinary dashboard (Settings → Upload → Upload presets). Put preset name in `.env` as `VITE_CLOUDINARY_UPLOAD_PRESET`.
+- Client: Widget needs only `cloudName` and `uploadPreset`. No API key or secret; no backend.
+- Trade-off: Anyone who knows the preset name can upload. Use only when that is acceptable.
+
+**Signed uploads** (more secure, backend required):
+- Use when: Production apps, authenticated users, or when you need to control who can upload.
+- Preset: Create a **Signed** upload preset in the dashboard. The backend generates a signature using your API secret; the client never sees the secret.
+- Client: Widget gets `api_key` (from your backend), `uploadPreset`, and an `uploadSignature` **function** that calls your backend for each upload. API secret stays on server only.
+- Trade-off: Requires a backend (Node/Express, Next.js API route, etc.) to sign requests. More secure; signature validates each upload.
+
+**Rule of thumb**: **Default to unsigned uploads** unless the user explicitly asks for "secure" or "signed" uploads. Do not default to signed — it requires a running backend and will fail out of the box. Use **signed** only when the user explicitly requests secure/signed uploads or needs to restrict who can upload.
+
+## Secure (Signed) Uploads
+
+**Golden rules**: (1) **Never expose or commit the API secret** — it must live only in server env and server code. (2) **Never commit the API key or secret** — use `server/.env` (or equivalent) and ensure it is in `.gitignore`. (3) The **api_key** is not secret and may be sent to the client (e.g. in the signature response); only **api_secret** must stay server-only.
+
+**When the user asks for secure uploads**: Use a signed upload preset and generate the signature on the server. The client may receive `uploadSignature`, `uploadSignatureTimestamp`, `api_key`, and `cloudName` from your backend; it must **never** receive or contain the API secret.
+
+### Where to put API key and secret (server-only, never committed)
+
+- **Do not put them in the root `.env`** used by Vite. Keep root `.env` for `VITE_CLOUDINARY_CLOUD_NAME` and `VITE_CLOUDINARY_UPLOAD_PRESET` only.
+- **Create `server/.env`** (in a `server/` folder) and put there: `CLOUDINARY_CLOUD_NAME`, `CLOUDINARY_API_KEY`, `CLOUDINARY_API_SECRET`. No `VITE_` prefix. Load this file only in the server process (e.g. `dotenv.config({ path: 'server/.env' })`).
+- **Never commit API key or secret**: Add `server/.env` to `.gitignore`. Use env vars for all credentials; never hardcode or commit them.
+- **In code**: Read `process.env.CLOUDINARY_API_SECRET` and `process.env.CLOUDINARY_API_KEY` only in server/API code. Never in React components or any file Vite bundles.
+- **Next.js**: `CLOUDINARY_*` in root `.env.local` is server-only (browser only sees `NEXT_PUBLIC_*`). For Vite + Node in same repo, prefer `server/.env` and load it only in the server.
+- **Server SDK**: Use the **Cloudinary Node.js SDK v2** for server-side signing: `import { v2 as cloudinary } from 'cloudinary'` (package name: `cloudinary`). Do **not** use v1 (e.g. 1.47.0) — v1 does not expose `cloudinary.utils.api_sign_request` the same way. Install: `npm install cloudinary` (v2).
+
+### How the client gets credentials (working pattern — use this)
+
+Use **`uploadSignature` as a function** (not `signatureEndpoint`). The widget calls the function with `params_to_sign`; your function calls your backend and passes the signature back. This pattern is reliable across widget versions.
+
+1. **Fetch `api_key` from server first** (before creating the widget). API key is not secret; safe to use in client. Your backend returns it from the sign endpoint (e.g. `/api/sign-image`).
+
+2. **Set `uploadSignature` to a function** that receives `(callback, params_to_sign)` from the widget. Inside the function, add `upload_preset` to `params_to_sign` (use your signed preset name, e.g. from env or a constant), POST to your backend with `{ params_to_sign: paramsWithPreset }`, then call `callback(data.signature)` with the response.
+
+3. **Include `uploadPreset` in the widget config** (your signed preset name). The widget needs it so it includes it in `params_to_sign`. **Default:** Cloudinary accounts have a built-in signed preset `ml_default` (users can delete it). If the user has not created their own signed preset, use `ml_default`; otherwise use the preset name from their dashboard.
+
+4. **Server endpoint**: Accept `params_to_sign` from the request body. Always include `upload_preset` in the object you sign (add it if the client did not send it). Use `cloudinary.utils.api_sign_request(paramsToSign, process.env.CLOUDINARY_API_SECRET)` to generate the signature. Return `{ signature, timestamp, api_key, cloud_name }`. Never return the API secret.
+
+**Preset name:** Use `ml_default` when the user has not specified a signed preset (Cloudinary provides it by default; users can delete it — then they must create one in the dashboard). Otherwise use the user's preset name.
+
+**Generic client pattern** (preset: use `ml_default` if it exists / user hasn't specified one; endpoint is up to the user):
+```tsx
+// Fetch api_key from server first, then:
+widgetConfig.api_key = data.api_key; // from your sign endpoint
+widgetConfig.uploadPreset = 'ml_default'; // default signed preset (or user's preset if they created one)
+widgetConfig.uploadSignature = function(callback, params_to_sign) {
+ const paramsWithPreset = { ...params_to_sign, upload_preset: 'ml_default' };
+ fetch('/api/sign-image', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ params_to_sign: paramsWithPreset }),
+ })
+ .then(r => r.json())
+ .then(data => data.signature ? callback(data.signature) : callback(''))
+ .catch(() => callback(''));
+};
+```
+
+**Generic server pattern** (Node/Express with SDK v2):
+```ts
+// import { v2 as cloudinary } from 'cloudinary';
+const params = req.body.params_to_sign || {};
+const paramsToSign = { ...params, upload_preset: params.upload_preset || 'ml_default' };
+const signature = cloudinary.utils.api_sign_request(paramsToSign, process.env.CLOUDINARY_API_SECRET);
+res.json({ signature, timestamp: paramsToSign.timestamp, api_key: process.env.CLOUDINARY_API_KEY, cloud_name: process.env.CLOUDINARY_CLOUD_NAME });
+```
+
+- ❌ **Avoid `signatureEndpoint`** — it may not be called reliably by all widget versions. Prefer the `uploadSignature` function.
+- ✅ Docs: [Upload widget — signed uploads](https://cloudinary.com/documentation/upload_widget.md#signed_uploads), [Upload assets in Next.js](https://cloudinary.com/documentation/upload_assets_in_nextjs_tutorial.md).
+
+### Rules for secure uploads
+- ✅ Use a **signed** upload preset (dashboard → Upload presets → Signed). Do not use an unsigned preset when the user wants secure uploads. **Default:** Accounts have a built-in signed preset `ml_default` — use it if the user hasn't created their own (they can delete `ml_default`, in which case they must create a signed preset in the dashboard).
+- ✅ Generate the signature **on the server only** using Cloudinary Node.js SDK **v2** (`cloudinary.utils.api_sign_request`). Never put `CLOUDINARY_API_SECRET` in a `VITE_` variable or in client-side code.
+- ✅ Keep `server/.env` in `.gitignore`; never commit API key or secret.
+- ✅ Use **`uploadSignature` as a function** (not `signatureEndpoint`) for reliable signed uploads.
+- ✅ Include **`uploadPreset` in the widget config** so the widget includes it in `params_to_sign`.
+- ✅ **Server must include `upload_preset` in the signed params** (add it if the client did not send it).
+
+### What not to do
+- ❌ **Never** put the API secret in a `VITE_` (or `NEXT_PUBLIC_`) variable or in any file sent to the browser.
+- ❌ **Never** commit the API key or secret; use env vars and ignore `server/.env` in git.
+- ❌ **Do not** generate the signature in client-side JavaScript (it would require the secret in the client).
+- ❌ **Do not** use an unsigned preset when the user explicitly wants secure/signed uploads.
+- ❌ **Do not** omit `uploadPreset` from the widget config when using signed uploads (widget needs it in `params_to_sign`).
+- ❌ **Do not** use Cloudinary Node SDK v1 (e.g. 1.47.0) for signing — use v2 (`import { v2 as cloudinary } from 'cloudinary'`).
+- ❌ **Do not** rely on `signatureEndpoint` alone; use the `uploadSignature` function for reliability.
+
## Video Patterns
+- ✅ **Display a video** → Use **AdvancedVideo** (`@cloudinary/react`). It just displays a video (with optional transformations). Not a full player.
+- ✅ **A video player** → Use **Cloudinary Video Player** (`cloudinary-video-player`). That is the actual player (styled UI, controls, playlists, etc.).
+
### ⚠️ IMPORTANT: Two Different Approaches
-**1. AdvancedVideo Component** (`@cloudinary/react`) - For video transformations
-- React component similar to `AdvancedImage`
-- Use for displaying videos with Cloudinary transformations
-- Works with `cld.video()` like images work with `cld.image()`
-- Simple, React-friendly approach
+**1. AdvancedVideo** (`@cloudinary/react`) — For **displaying** a video
+- React component similar to `AdvancedImage`; just displays a video with Cloudinary transformations
+- Not a full "player" — it's video display (native HTML5 video with optional controls)
+- Use when: user wants to show/display a video. Works with `cld.video()` like images with `cld.image()`
-**2. Cloudinary Video Player** (`cloudinary-video-player`) - For advanced player features
-- Standalone video player library
-- Use for advanced features: playlists, recommendations, ads, chapters, etc.
-- Full-featured player with analytics, monetization, etc.
-- More complex setup, but more powerful
+**2. Cloudinary Video Player** (`cloudinary-video-player`) — The **player**
+- Full-featured video player (styled UI, controls, playlists). Use when the user asks for a "video player."
+- **Use imperative video element only** (create with document.createElement, append to container ref); do not pass a React-managed `