Cloudinary
Image upload and transformations
git clone https://github.com/bijectionhq/cloudinary.git
cd cloudinary
git checkout 3897f008668b38b4326b484a2ef2789f4b2c05edBijection repository access is required. Request access.
Description
Image upload and transformations
Explore the package README for API concepts, configuration, and examples. Check the Bijection source for adaptation-specific setup.
Review the source and its dependencies before adopting this package. Source availability does not establish npm publication or runtime qualification.
Getting started
- Get the source at the revision above and read its package documentation.
- Check the required Bijection SDK version, dependencies, and external service credentials.
- Follow the package setup instructions and the guide to using components.
- Exercise the component in your deployment according to your project’s verification rules.
Open source
Maintained source: bijectionhq/cloudinary. Based on imaxisXD/cloudinary-convex. License: Apache-2.0. Consult the repository for license terms, notices, and contributor attribution.
From the upstream publisher
A Cloudinary integration component for Convex providing image upload, transformation, and management capabilities.
Explore the demo ↗Upstream overview
From the upstream package documentation.
This component integrates Cloudinary's image management APIs directly into Convex, providing both base64 uploads for small files and direct uploads that bypass Convex's 16MB limit for large files up to 100MB. It automatically tracks assets in your Convex database with optimized indexes, generates secure upload signatures server-side, and provides full TypeScript support with exported validators. The component offers both a high-level makeCloudinaryAPI approach for React apps and a lower-level CloudinaryClient for custom server logic.
Use cases
From the upstream package documentation.
- Building a photo sharing app where users upload high-resolution images that exceed typical serverless limits
- Creating a content management system that needs real-time asset tracking synchronized between Cloudinary and your database
- Implementing dynamic image transformations with type-safe parameters for e-commerce product galleries
- Building an admin dashboard that requires secure server-side image uploads with progress tracking
- Developing a social media platform where you need both small profile pictures and large media uploads
How it works
From the upstream package documentation.
The component provides two main integration patterns: makeCloudinaryAPI creates public functions in your Convex app that React clients can call directly, while CloudinaryClient gives you fine-grained control within Convex actions and queries. For uploads, it supports both base64 encoding for files under 10MB and direct uploads to Cloudinary that bypass Convex entirely for larger files.
Asset metadata gets automatically stored in Convex with optimized indexes, allowing you to use useQuery for real-time asset lists and management. The component includes React hooks like useCloudinaryUpload and useCloudinaryImage that work with your app's API functions, providing upload progress tracking and transformation capabilities.
Setup requires adding the component to your convex.config.ts with app.use(cloudinary) and configuring your Cloudinary credentials as environment variables. The component handles secure signature generation server-side and provides full TypeScript support with validators like vAssetResponse for type-safe data flow.
Use with agents and CLI
Give your coding agent the component’s source identity and setup guidance.
Help me integrate Cloudinary with Bijection.
Read: https://bijection.com/components/cloudinary/SKILL.md
Check SDK compatibility and dependency availability.
Follow this project's execution and verification rules.Upstream README.md
View repository ↗Documentation from imaxisXD/cloudinary-convex, recorded October 23, 2026. APIs and setup may differ in the Bijection adaptation.
Cloudinary Component for Convex
A Convex Component for Cloudinary integration that provides image upload, transformation, and management capabilities using direct Cloudinary REST APIs with full TypeScript support.
Features
- Direct API Integration: Uses Cloudinary REST APIs directly instead of SDKs for better control and reduced dependencies.
- Two Upload Methods:
- Base64 Upload: Simple flow for small files (<10MB).
- Direct Upload: Bypasses Convex 16MB limit for large files (100MB+) with real-time progress.
- Asset Management: Automatic asset tracking in Convex with optimized indexes.
- Dynamic Transformations: Generate transformed URLs on-the-fly.
- Type Safety: Full TypeScript support with exported validators (
vAssetResponse) and inferred types. - Secure: Server-side signature generation and environment-based credentials.
Architecture
graph TD
Client["Client Browser"] -->|"Action/Query"| Convex["Convex Component"]
Convex -->|"API Call"| Cloudinary["Cloudinary API"]
Client -->|"Direct Upload (Large Files)"| Cloudinary
Cloudinary -->|"Webhook/Response"| Convex
Simple and robust data flow ensuring your assets are always in sync with your database.
Prerequisites
- Convex Project: An existing Convex project (Setup Guide)
- Cloudinary Account: Free account at cloudinary.com
Installation
npm install @imaxis/cloudinary-convex
Setup
1. Configure Convex
Create or update your convex.config.ts:
// convex/convex.config.ts
import { defineApp } from "convex/server";
import cloudinary from "@imaxis/cloudinary-convex/convex.config";
const app = defineApp();
app.use(cloudinary);
export default app;
2. Set Environment Variables
Get credentials from Cloudinary Console:
npx convex env set CLOUDINARY_CLOUD_NAME <your_cloud_name>
npx convex env set CLOUDINARY_API_KEY <your_api_key>
npx convex env set CLOUDINARY_API_SECRET <your_api_secret>
Quick Start
Option 1: Using makeCloudinaryAPI (Recommended for React Apps)
This approach creates public API functions that can be called from React clients.
Backend (convex/cloudinary.ts):
import { makeCloudinaryAPI } from "@imaxis/cloudinary-convex";
import { components } from "./_generated/api";
// Export all API functions - uses environment variables automatically
export const {
upload,
transform,
deleteAsset,
listAssets,
getAsset,
updateAsset,
generateUploadCredentials,
finalizeUpload,
} = makeCloudinaryAPI(components.cloudinary);
React Client:
import { api } from "../convex/_generated/api";
import { useQuery, useAction } from "convex/react";
import { useCloudinaryUpload } from "@imaxis/cloudinary-convex/react";
function ImageGallery() {
// List images
const images = useQuery(api.cloudinary.listAssets, { limit: 20 });
// Upload with progress tracking
const { upload, isUploading, progress } = useCloudinaryUpload(
api.cloudinary.upload
);
const handleUpload = async (file: File) => {
const base64 = await fileToBase64(file);
const result = await upload(base64, { folder: "uploads" });
console.log("Uploaded:", result.secureUrl);
};
return (
<div>
{isUploading && <p>Uploading... {progress}%</p>}
{images?.map((img) => (
))}
</div>
);
}
Option 2: Using CloudinaryClient (For Server-Side Logic)
This approach is ideal when you need more control or want to build custom logic.
// convex/images.ts
import { action, query } from "./_generated/server";
import { components } from "./_generated/api";
import { CloudinaryClient, vAssetResponse } from "@imaxis/cloudinary-convex";
import { v } from "convex/values";
// Initialize client - uses environment variables automatically
const cloudinary = new CloudinaryClient(components.cloudinary);
// Upload Action (Base64)
export const uploadImage = action({
args: { base64Data: v.string() },
handler: async (ctx, args) => {
return await cloudinary.upload(ctx, args.base64Data, {
folder: "uploads",
tags: ["user-content"],
});
},
});
// List Images Query
export const getImages = query({
args: {},
returns: v.array(vAssetResponse),
handler: async (ctx) => {
return await cloudinary.list(ctx, { limit: 20 });
},
});
React Hooks
The component provides React hooks for common operations. Important: These hooks require function references from your app's API (created via makeCloudinaryAPI), not the component directly.
Available Hooks
import {
useCloudinaryUpload,
useCloudinaryImage,
useCloudinaryAssets,
useCloudinaryAsset,
useCloudinaryOperations,
useUploadStatus,
usePendingUploads,
} from "@imaxis/cloudinary-convex/react";
Upload Hook
import { api } from "../convex/_generated/api";
function UploadButton() {
const { upload, isUploading, progress, error, reset } = useCloudinaryUpload(
api.cloudinary.upload
);
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
try {
const result = await upload(file, { folder: "uploads" });
console.log("Upload complete:", result);
} catch (err) {
console.error("Upload failed:", err);
}
}
};
return (
<div>
<input type="file" onChange={handleFileChange} disabled={isUploading} />
{isUploading && <p>Uploading... {progress}%</p>}
{error && <p style={{ color: "red" }}>{error}</p>}
</div>
);
}
Image Transformation Hook
function TransformedImage({ publicId }: { publicId: string }) {
const { transformedUrl, isLoading } = useCloudinaryImage(
api.cloudinary.transform,
publicId,
{ width: 300, height: 300, crop: "fill" }
);
if (isLoading) return <div>Loading...</div>;
return <img src={transformedUrl} alt="" />;
}
Asset Operations Hook
function AssetManager({ publicId }: { publicId: string }) {
const { deleteAsset, updateAsset } = useCloudinaryOperations(
api.cloudinary.deleteAsset,
api.cloudinary.updateAsset
);
const handleDelete = async () => {
await deleteAsset(publicId);
};
const handleUpdateTags = async () => {
await updateAsset(publicId, { tags: ["featured", "hero"] });
};
return (
<div>
<button onClick={handleUpdateTags}>Add Tags</button>
<button onClick={handleDelete}>Delete</button>
</div>
);
}
Using with Cloudinary React SDK
For advanced image rendering features like responsive images, lazy loading, and automatic format optimization, we recommend using Cloudinary's official React SDK alongside this component.
Install Cloudinary React SDK:
npm install @cloudinary/react @cloudinary/url-gen
Use your component for data, Cloudinary SDK for rendering:
import { useQuery } from "convex/react";
import { api } from "../convex/_generated/api";
import { Cloudinary } from "@cloudinary/url-gen";
import { AdvancedImage, lazyload, responsive } from "@cloudinary/react";
// Initialize Cloudinary (client-side only needs cloud name)
const cld = new Cloudinary({ cloud: { cloudName: "your-cloud-name" } });
function ImageGallery() {
// Your component: reactive asset list from Convex
const assets = useQuery(api.cloudinary.listAssets, { limit: 20 });
return (
<div className="gallery">
{assets?.map((asset) => (
<AdvancedImage
key={asset.publicId}
cldImg={cld.image(asset.publicId)}
plugins={[lazyload(), responsive()]}
// Cloudinary SDK handles:
// - Responsive sizing
// - Lazy loading
// - Automatic format (WebP/AVIF)
// - Retina displays
/>
))}
</div>
);
}
Benefits of this approach:
| This Component | Cloudinary React SDK |
|---|---|
| Stores asset metadata in Convex | Optimized image rendering |
Real-time queries with useQuery | Responsive images |
| Secure server-side uploads | Lazy loading |
| Two upload methods (base64 + direct) | Automatic format selection (WebP/AVIF) |
| Asset management (delete, update, list) | Blur placeholders |
Image Transformations
The component provides fully type-safe transformation options with IntelliSense support. All transformation parameters are documented with JSDoc comments for easy discovery.
Basic Usage
// In React with the transform hook
const { transformedUrl } = useCloudinaryImage(
api.cloudinary.transform,
"my-image-id",
{
width: 300,
height: 200,
crop: "fill",
gravity: "auto",
quality: "auto",
format: "auto",
}
);
// Or with CloudinaryClient in Convex
const result = await cloudinary.transform(ctx, "my-image-id", {
width: 300,
height: 200,
crop: "fill",
});
Transformation Options Reference
Dimensions
| Option | Type | Description |
|---|---|---|
width | number | Output width in pixels |
height | number | Output height in pixels |
Crop Modes
| Mode | Description |
|---|---|
"fill" | Scale and crop to fill exact dimensions (may crop edges) |
"fit" | Scale to fit within dimensions (maintains aspect ratio) |
"crop" | Extract region (requires gravity or x/y coordinates) |
"thumb" | Generate thumbnail using face detection |
"scale" | Resize without maintaining aspect ratio (may distort) |
"pad" | Resize and pad with background color to exact dimensions |
"limit" | Like fit, but only scales down (never enlarges) |
"fill_pad" | Fill with smart content-aware padding |
"auto" | Automatic cropping based on content |
Gravity Options
| Gravity | Description |
|---|---|
"auto" | AI-powered detection of important content |
"face" | Focus on detected face |
"faces" | Focus on multiple faces |
"auto:subject" | Focus on main subject |
"center" | Center of the image |
"north" | Top of the image |
"south" | Bottom of the image |
"east" | Right side |
"west" | Left side |
"north_east" | Top-right corner |
"south_west" | Bottom-left corner |
Quality & Format
| Option | Values | Description |
|---|---|---|
quality | "auto", "auto:best", "auto:eco", 1-100 | Compression quality |
format | "auto", "webp", "avif", "jpg", "png" | Output format |
dpr | 1, 2, 3, "auto" | Device pixel ratio for retina displays |
Visual Effects
| Option | Examples | Description | | --------- | -------------------------------
[README truncated for prompt length]