How to Attach a Blob of Image to the Editor
Yes, this is absolutely possible! You're on the right track. Let me walk you through how to upload a browser-generated blob as an actual image asset to Sanity.
The Solution
You can create a custom button or action that:
- Generates your OG image as a blob in the browser
- Uses
client.assets.upload()to upload it to Sanity's CDN - Attaches the resulting asset to your image field
Implementation Example
Here's how you could implement this:
import {useClient} from 'sanity'
function GenerateOGImageButton({documentId}) {
const client = useClient({apiVersion: '2025-02-06'})
const handleGenerateOGImage = async () => {
// 1. Generate your OG image as a blob (using canvas, html2canvas, etc.)
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
// ... your image generation logic here
// 2. Convert canvas to blob
const blob = await new Promise(resolve =>
canvas.toBlob(resolve, 'image/png')
)
// 3. Upload to Sanity
const asset = await client.assets.upload('image', blob, {
filename: `og-image-${documentId}.png`,
contentType: 'image/png'
})
// 4. Update your document with the new image asset reference
await client
.patch(documentId)
.set({
ogImage: {
_type: 'image',
asset: {
_type: 'reference',
_ref: asset._id
}
}
})
.commit()
console.log('OG image uploaded!', asset)
}
return (
<button onClick={handleGenerateOGImage}>
Generate og:image
</button>
)
}Using as a Custom Asset Source
If you want to integrate this more deeply into the Studio's image field UI, you can create a custom asset source. Here's a simplified example:
// customOGImageSource.js
import React, {useCallback} from 'react'
import {Dialog, Card} from '@sanity/ui'
export default function OGImageGenerator({onSelect, onClose, document}) {
const handleGenerate = useCallback(async () => {
// Generate your blob
const blob = await generateOGImageBlob(document)
// Return it to Sanity's asset system
onSelect([{
kind: 'file', // Using 'file' kind for blob/File objects
value: blob,
assetDocumentProps: {
originalFilename: `og-image-${document._id}.png`,
source: {
name: 'og-generator',
id: document._id
}
}
}])
}, [onSelect, document])
return (
<Dialog header="Generate OG Image" onClose={onClose} open>
<Card padding={4}>
<button onClick={handleGenerate}>Generate</button>
</Card>
</Dialog>
)
}
// Asset source plugin definition
export const ogImageAssetSource = {
name: 'og-generator',
title: 'Generate OG Image',
component: OGImageGenerator,
icon: () => 'πΌοΈ'
}Then add it to your schema field:
{
name: 'ogImage',
type: 'image',
options: {
sources: [ogImageAssetSource]
}
}Key Points from the Documentation
Based on the custom asset sources documentation:
kind: 'file': When usingonSelectin a custom asset source, usekind: 'file'for blob/File objects. The Studio will automatically handle uploading it to Sanity's CDN.client.assets.upload(): This method (documented in the Sanity client GitHub repo) handles uploading blobs directly from the browser and returns an asset document- Asset reference structure: The uploaded asset needs to be referenced properly with
{_type: 'image', asset: {_type: 'reference', _ref: assetId}}
CORS Consideration
If you're using kind: 'url' instead of kind: 'file', note that the resource must respond with an access-control-allow-origin header that allows the image to be read by the Studio host.
The blob will be automatically uploaded to Sanity's CDN, and you'll get back a proper asset document that you can reference in your image field! This is exactly what you're looking for in step (4) of your workflow.
Sanity β Build the way you think, not the way your CMS thinks
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.