Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .cursor/rules/cloudinary.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
description: Cloudinary frontend-frameworks — agent guide
alwaysApply: true
---

Read and follow `AGENTS.md` in the repository root. It is the single
authoritative guide for this package: build/test commands, conventions,
gotchas, and when to use this SDK versus a sibling Cloudinary package.
5 changes: 5 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Cloudinary frontend-frameworks — instructions for AI coding agents

Read `AGENTS.md` in the repository root and follow it. It is the single
authoritative guide for this package: build/test commands, conventions,
gotchas, and when to use this SDK versus a sibling Cloudinary package.
87 changes: 87 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# AGENTS.md — frontend-frameworks

## What this monorepo is (one line)
The current Cloudinary React/Angular/Vue **component** SDKs — `<AdvancedImage>` / `<AdvancedVideo>` plus plugins — built on `@cloudinary/url-gen`. A Lerna 4 monorepo (independent versioning) publishing `@cloudinary/react`, `@cloudinary/ng` (Angular), `@cloudinary/vue`, and the shared `@cloudinary/html` layer.

## When to use this / when NOT to use this
- **Use this when:** you're rendering media as a **React, Angular, or Vue component** and want `<AdvancedImage>` / `<AdvancedVideo>` with plugins (`lazyload`, `responsive`, `accessibility`, `placeholder`) instead of hand-writing `<img>` / `<video>` tags.
- **Do NOT use this when:** you only need to **build delivery URLs** (use `@cloudinary/url-gen` directly), you're on **Next.js** (use `next-cloudinary`), or your code runs on a **server or build step** and needs uploads, the Admin API, or signed URLs (use `cloudinary`, the Node.js SDK that holds the `API_SECRET`).
- **Successor note:** these packages **supersede** the legacy `cloudinary-react`, `cloudinary_angular`, and `cloudinary-vue` repos. Don't pick the legacy packages for new code.
- **Sibling packages:** `@cloudinary/url-gen` (repo `js-url-gen`) = base browser-safe URL builder every package here sits on; `next-cloudinary` = Next.js drop-in components; `cloudinary` (repo `cloudinary_npm`) = server-side SDK.

## Setup
Consumer install (pick one framework, alongside the base URL builder):
```bash
# React
npm install @cloudinary/url-gen @cloudinary/react
# Angular
npm install @cloudinary/url-gen @cloudinary/ng
# Vue
npm install @cloudinary/url-gen @cloudinary/vue
```
Working on the monorepo itself (Lerna 4, root Node engine `>=14.0.0 <19`):
```bash
npm ci # install (CI uses npm ci)
npm run quickstart # lerna bootstrap + lerna run build (first-time setup)
```
No credentials at install — `@cloudinary/url-gen` builds public delivery URLs in the browser. Configure with a cloud name:
```js
const cld = new Cloudinary({ cloud: { cloudName: '<CLOUD_NAME>' } });
```

## Minimal runnable example
```jsx
import React from 'react';
import { Cloudinary } from '@cloudinary/url-gen';
import { AdvancedImage, responsive, accessibility } from '@cloudinary/react';

const cld = new Cloudinary({ cloud: { cloudName: 'demo' } });

export const App = () => {
const img = cld.image('sample');
return <AdvancedImage cldImg={img} plugins={[responsive(), accessibility()]} />;
};
```
Plugin order, when using several: `[lazyload(), responsive(), accessibility(), placeholder()]` (omit any, keep the order). The media object is passed via `cldImg` on `<AdvancedImage>` and `cldVid` on `<AdvancedVideo>`.

## Build / test commands (run these after editing)
Lerna fans each command out across packages; per-package tooling is heterogeneous (rollup + tsc for html/react, `@vue/cli-service` for vue, Angular CLI / ng-packagr for angular).
```bash
npm ci # install
npm run build # lerna run build (each SDK builds the shared ../html layer first)
npm run test # lerna run test — run after any change to a package's src
npm run lint # lerna run lint
npm run typecheck # lerna run typecheck
```
Single test (per package — Lerna has no cross-package name filter):
```bash
cd packages/react && npx jest -t "<test name>" # react (Jest 27)
cd packages/html && npx jest -t "<test name>" # html (Jest 29)
cd packages/vue && npx vue-cli-service test:unit -t "<test name>" # vue
# angular uses Karma + Jasmine — focus a spec with fdescribe/fit (no CLI -t flag)
```
CI (`.github/workflows/pull-request.yml`) runs on Node **14, 16, 18** and executes, in order: `npm ci` → `npm run test` → `npm run lint` → `npm run typecheck`. Keep all four green.

## Conventions & gotchas
- **The published Angular package is `@cloudinary/ng`.** The workspace folder is `packages/angular` (private `@cloudinary/angular-workspace`, Angular CLI 12); the published library manifest lives at `packages/angular/projects/cloudinary-library/`. Never reference `@cloudinary/angular` on npm — that's an abandoned Beta (1.0.0-beta.14) that points here.
- **`@cloudinary/html` is the shared layer.** `@cloudinary/react`, `@cloudinary/ng`, and `@cloudinary/vue` all depend on it at runtime; it holds the real plugin implementations and the `HtmlImageLayer` / `HtmlVideoLayer` DOM wrappers. Change behavior once there and it lands everywhere.
- **`packages/vue3/` is a README-only stub** — no `package.json`, no source. The published Vue package is `packages/vue` (`@cloudinary/vue`). Don't edit or wire up `vue3/`.
- **`@cloudinary/url-gen` is a companion, not bundled.** It's a devDependency in each package; consumers install it alongside the framework package.
- **Edit source, not built output.** Work in each package's `src/` (or the Angular `projects/cloudinary-library/src/`), never in `dist/`. Every SDK's build recompiles the shared `../html` layer first.
- **Component / plugin surface:** `AdvancedImage`, `AdvancedVideo`, and plugins `lazyload`, `responsive`, `accessibility`, `placeholder`. Verify any other symbol against the package source before using it.
- **Framework support** (from `peerDependencies`): React `^16.3.0 || ^17 || ^18 || ^19`; Angular `@angular/core` / `@angular/common` `>=12.0.0`; Vue 3.
- **No secrets here.** This is browser-side rendering — there is no `API_SECRET`. Anything requiring one belongs in the server SDK.

## Canonical docs (leave the repo for depth)
- React guide: https://cloudinary.com/documentation/react2_integration
- Angular guide: https://cloudinary.com/documentation/angular2_integration
- Vue.js guide: https://cloudinary.com/documentation/vue_integration
- url-gen (base package): https://github.com/cloudinary/js-url-gen
- Transformation & API references: https://cloudinary.com/documentation/cloudinary_references
- MCP server (agent/no-code path): https://github.com/cloudinary/mcp-servers

## Agent / MCP note
If a capability is also exposed via the Cloudinary MCP servers, prefer the MCP tool for autonomous task execution and use these SDKs for code generation. See cloudinary/mcp-servers.

## Commit / PR conventions
- Branch off and PR against `master`. CI must pass on Node 14/16/18: `npm run test`, `npm run lint`, `npm run typecheck`.
39 changes: 39 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
@AGENTS.md

# CLAUDE.md — frontend-frameworks

## Claude Code-specific notes

**Primary reference:** `AGENTS.md` (imported above) covers setup, build/test commands, conventions, and gotchas. Read it before touching any file.

## What this repo is

A Lerna 4 monorepo (independent versioning) publishing Cloudinary's browser **component** SDKs: `@cloudinary/react`, `@cloudinary/ng` (Angular), and `@cloudinary/vue`, plus the shared `@cloudinary/html` layer. Each renders `<AdvancedImage>` / `<AdvancedVideo>` on top of `@cloudinary/url-gen`. This is browser-side rendering — there is no `API_SECRET` in this repo.

## Key constraints

- **The Angular package is `@cloudinary/ng`** — never `@cloudinary/angular` (an abandoned Beta on npm). The published library lives at `packages/angular/projects/cloudinary-library/`; `packages/angular/package.json` is the private Angular CLI 12 workspace.
- **`packages/vue3/` is a README-only stub** (no source). The published Vue package is `packages/vue` (`@cloudinary/vue`).
- **`@cloudinary/html` is the shared layer** all three framework SDKs depend on at runtime — it holds the plugin implementations and the DOM layer wrappers. Behavior changes there propagate everywhere.
- **Edit `src/`, not `dist/`.** Each SDK's build recompiles the shared `../html` layer first.
- **`@cloudinary/url-gen` is a companion install**, not bundled — examples install it alongside the framework package.
- **Component / plugin surface:** `AdvancedImage`, `AdvancedVideo`, and plugins `lazyload`, `responsive`, `accessibility`, `placeholder`. Verify any other symbol against package source. The media object is passed via `cldImg` (`<AdvancedImage>`) / `cldVid` (`<AdvancedVideo>`); plugin array order matters.

## Verified build/test commands

```bash
npm ci # install (CI uses npm ci)
npm run quickstart # lerna bootstrap + build all packages (first-time setup)
npm run build # lerna run build
npm run test # lerna run test — run after any src change
npm run lint # lerna run lint
npm run typecheck # lerna run typecheck

# Single test (per package — no cross-package name filter):
cd packages/react && npx jest -t "<test name>"
cd packages/vue && npx vue-cli-service test:unit -t "<test name>"
cd packages/html && npx jest -t "<test name>"
# angular: Karma + Jasmine — focus a spec with fdescribe/fit
```

CI (`.github/workflows/pull-request.yml`) runs on Node 14/16/18: `npm ci` → `npm run test` → `npm run lint` → `npm run typecheck`. Keep all green. Branch off and PR against `master`.
181 changes: 122 additions & 59 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,83 +1,146 @@
# Cloudinary Frontend Frameworks
# Cloudinary frontend framework SDKs

## About this project
[![license](https://img.shields.io/github/license/cloudinary/frontend-frameworks.svg)](https://github.com/cloudinary/frontend-frameworks/blob/master/LICENSE)
[![CI](https://github.com/cloudinary/frontend-frameworks/actions/workflows/pull-request.yml/badge.svg)](https://github.com/cloudinary/frontend-frameworks/actions/workflows/pull-request.yml)

This project contains SDKs designed to work with [Cloudinary url-gen](https://github.com/cloudinary/js-url-gen). </br>
These SDKs render CloudinaryImage or CloudinaryVideo objects into the DOM.
This monorepo publishes Cloudinary's browser component SDKs — `@cloudinary/react` (1.14.4), `@cloudinary/ng` (2.1.6, the Angular package), and `@cloudinary/vue` (1.13.4, Vue 3) — plus the shared `@cloudinary/html` (1.13.5) layer they build on. Each renders Cloudinary images and video as framework components, `<AdvancedImage>` and `<AdvancedVideo>`, on top of the [`@cloudinary/url-gen`](https://github.com/cloudinary/js-url-gen) URL builder, which runs in the browser and holds no API secret. `@cloudinary/react` supports React `^16.3.0 || ^17 || ^18 || ^19`; `@cloudinary/ng` supports `@angular/core` and `@angular/common` `>=12.0.0`; `@cloudinary/vue` supports Vue 3.

### Packages contained within this project:
<br />

- The React SDK used to render an image & video component. [NPM](https://www.npmjs.com/package/@cloudinary/react) | {@link ReactSDK|Reference}
- The Angular SDK used to render an image & video component. [NPM](https://www.npmjs.com/package/@cloudinary/ng
) | {@link AngularSDK|Reference}


Each SDK also contains advanced features in the form of plugins, which extend the native HTMLImage and HTMLVideo elements.
## Installation

- {@link accessibility|accessibility} - Used to make your images more accessible to your users with visual disabilities.
- {@link lazyload|lazyload} - Used to delay loading images if they are not yet visible on the screen.
- {@link placeholder|placeholder} - Used to display a lightweight version of an image while the target image is downloading.
- {@link responsive|responsive} - Used to resize your images automatically based on the viewport size.
Install the framework package you need alongside the base `@cloudinary/url-gen`.

## Development setup
To build and link project:
- clone project
- npm run quickstart
React:

```bash
npm install @cloudinary/url-gen @cloudinary/react
```

Angular (the published package is `@cloudinary/ng`, not `@cloudinary/angular`):

## Installation
To get started, install the npm client package of your choice along with our base package.
For example, to use Cloudinary in a [React](https://cloudinary.com/documentation/react2_integration) environment, the following packages should be installed:

```bash
npm i @cloudinary/react @cloudinary/url-gen
npm install @cloudinary/url-gen @cloudinary/ng
```

**Note**: To use [Angular](https://cloudinary.com/documentation/angular2_integration) install `@cloudinary/ng`.
Vue:

```bash
npm install @cloudinary/url-gen @cloudinary/vue
```

## Configuration

These packages build public delivery URLs in the browser and take no API key or API secret. Configure a `Cloudinary` instance from `@cloudinary/url-gen` with your cloud name (find it in the Console dashboard):

```js
import { Cloudinary } from '@cloudinary/url-gen';

const cld = new Cloudinary({ cloud: { cloudName: '<CLOUD_NAME>' } });
```

The config shape is `{ cloud: { cloudName: '<CLOUD_NAME>' } }` — not the `CLOUDINARY_URL` environment variable, which the server-side SDKs use. Uploads, the Admin API, and signed URLs need the API secret and belong on a server; use the [`cloudinary`](https://github.com/cloudinary/cloudinary_npm) Node.js SDK there, and keep the API secret out of client-side code and version control.

## Quick examples

## Simple usage
The following is a simple example using [React](https://cloudinary.com/documentation/react2_integration).
For more information on React and other frameworks, navigate to the specific reference using the **Packages** menu.
```javascript
import React from 'react'
// Cloudinary is used to configure your account and generate urls for your media assets
import {Cloudinary} from "@cloudinary/url-gen";
// Import the cloudinary media component (AdvancedImage / AdvancedVideo),
// and the plugins you want to use.
import {AdvancedImage, accessibility, responsive} from '@cloudinary/react';
### Transform and optimize an image (React)

// Once per project/app - configure your instance.
// See the documentation of @cloudinary/url-gen for more information.
const myCld = new Cloudinary({cloud: {cloudName: 'demo'}});
Build a `CloudinaryImage` with `cld.image()`, apply transformations, then pass it to `<AdvancedImage>` as `cldImg`. This resizes to a 100x150 fill crop and lets Cloudinary pick the format and quality for the requesting browser (`f_auto`, `q_auto`):

```jsx
import React from 'react';
import { Cloudinary } from '@cloudinary/url-gen';
import { fill } from '@cloudinary/url-gen/actions/resize';
import { format, quality } from '@cloudinary/url-gen/actions/delivery';
import { auto } from '@cloudinary/url-gen/qualifiers/format';
import { auto as autoQuality } from '@cloudinary/url-gen/qualifiers/quality';
import { AdvancedImage } from '@cloudinary/react';

const cld = new Cloudinary({ cloud: { cloudName: 'demo' } });

export const App = () => {
// Create a new image object:
const img = myCld.image('sample');

// Render your component.
return (
<div>
<AdvancedImage cldImg={img} plugins={[responsive(), accessibility()]}/>
</div>
)
const img = cld.image('sample')
.resize(fill().width(100).height(150))
.delivery(format(auto()))
.delivery(quality(autoQuality()));

return <AdvancedImage cldImg={img} />;
// renders https://res.cloudinary.com/demo/image/upload/c_fill,h_150,w_100/f_auto/q_auto/sample
};
```

## Plugin Order
### Render an image with plugins (Angular)

Register `CloudinaryModule` from `@cloudinary/ng`, then use the `<advanced-image>` component with a `cldImg` input. Plugins are passed to the `plugins` input as an array, and their order matters (recommended: `lazyload`, `responsive`, `accessibility`, `placeholder`):

```ts
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { CloudinaryModule } from '@cloudinary/ng';
import { AppComponent } from './app.component';

@NgModule({
imports: [BrowserModule, CloudinaryModule],
declarations: [AppComponent],
bootstrap: [AppComponent],
})
export class AppModule {}
```

```ts
// app.component.ts
import { Component } from '@angular/core';
import { Cloudinary } from '@cloudinary/url-gen';
import { lazyload, responsive } from '@cloudinary/ng';

@Component({
selector: 'app-root',
template: `<advanced-image [cldImg]="img" [plugins]="plugins"></advanced-image>`,
})
export class AppComponent {
cld = new Cloudinary({ cloud: { cloudName: 'demo' } });
img = this.cld.image('sample'); // renders .../demo/image/upload/sample
plugins = [lazyload(), responsive()];
}
```

### Render a video (Vue)

Build a `CloudinaryVideo` with `cld.video()` and render it with `<AdvancedVideo>`, passing the media object via the `cldVid` prop. Native video attributes such as `controls` pass straight through to the underlying `<video>` element:

<div>
We recommend the following order when using our plugins to achieve the best results:
<br/><br/>
```vue
<script setup>
import { Cloudinary } from '@cloudinary/url-gen';
import { AdvancedVideo } from '@cloudinary/vue';

```javascript
<AdvancedImage plugins={[lazyload(), responsive(), accessibility(), placeholder()]}/>
const cld = new Cloudinary({ cloud: { cloudName: 'demo' } });
const vid = cld.video('dog');
// plays https://res.cloudinary.com/demo/video/upload/dog
</script>

<template>
<AdvancedVideo :cldVid="vid" controls />
</template>
```

You can omit any plugin, but the order from above should remain.
</div>
## For AI agents

These are the current Cloudinary React, Angular, and Vue component SDKs — `@cloudinary/react`, `@cloudinary/ng`, and `@cloudinary/vue` — for rendering media as framework components in the browser. They build on the base `@cloudinary/url-gen` URL builder and supersede the legacy class-based repos `cloudinary-react`, `cloudinary_angular`, and `cloudinary-vue`; don't pick those for new code. The Angular package is `@cloudinary/ng` — `@cloudinary/angular` (stuck at 1.0.0-beta.14) is an abandoned Beta that points to `@cloudinary/ng`. For tasks these packages don't cover, route to the correct sibling:

| Task | Package |
|---|---|
| Build delivery URLs with no component layer | [`@cloudinary/url-gen`](https://github.com/cloudinary/js-url-gen) |
| Drop-in Next.js components | [`next-cloudinary`](https://github.com/cloudinary-community/next-cloudinary) |
| Server-side upload, Admin API, signed URLs (holds the API secret) | [`cloudinary`](https://github.com/cloudinary/cloudinary_npm) |
| Run Cloudinary operations as agent tools | [Cloudinary MCP servers](https://github.com/cloudinary/mcp-servers) |

## Links

## Contributors
- [React SDK guide](https://cloudinary.com/documentation/react2_integration)
- [Angular SDK guide](https://cloudinary.com/documentation/angular2_integration)
- [Vue.js SDK guide](https://cloudinary.com/documentation/vue_integration)
- [`@cloudinary/url-gen` (base URL builder)](https://github.com/cloudinary/js-url-gen)
- [Transformation and API references](https://cloudinary.com/documentation/cloudinary_references)
- [Documentation llms.txt index](https://cloudinary.com/documentation/llms.txt)
- Packages on npm: [`@cloudinary/react`](https://www.npmjs.com/package/@cloudinary/react) · [`@cloudinary/ng`](https://www.npmjs.com/package/@cloudinary/ng) · [`@cloudinary/vue`](https://www.npmjs.com/package/@cloudinary/vue)

Repository is using [Conventional Commits](https://www.conventionalcommits.org/). To publish packages please read instructions in [sdk-scripts](https://github.com/CloudinaryLtd/sdk-scripts/blob/master/src/release/js/frontend-frameworks/README.md).
Released under the MIT license.