How to attach browser-generated blob as image to Sanity editor?
1. On the editor, I put a title and a description
2. There is a button called "Generate og:image"
3. If I click it, it creates a blob of image on the browser
4. It is attached as an actual image
5. It will be uploaded to sanity cdn.
6. During the build time (or on the server) the url is used for 'og:image' tag.
I'm curious about (4). I might need to dig more into sources of image type. But has anyone done something similar already?
https://www.sanity.io/docs/image-type#sources-d82cf03b063c
Yes, this is absolutely possible! You can generate an image in the browser (like an og:image), convert it to a blob, and upload it directly to Sanity's CDN using the client.assets.upload() method.
Here's how to accomplish what you're describing:
Uploading a browser-generated blob to Sanity
import {createClient} from '@sanity/client'
const client = createClient({
projectId: 'your-project-id',
dataset: 'your-dataset',
token: process.env.SANITY_WRITE_TOKEN, // You'll need a write token
apiVersion: '2025-02-06',
useCdn: false
})
// 1. Generate your og:image (example using canvas)
async function generateOgImage(title, description) {
const canvas = document.createElement('canvas')
canvas.width = 1200
canvas.height = 630
const ctx = canvas.getContext('2d')
// Draw your og:image content
ctx.fillStyle = '#000'
ctx.fillRect(0, 0, 1200, 630)
ctx.fillStyle = '#fff'
ctx.font = '48px sans-serif'
ctx.fillText(title, 50, 100)
// 3. Convert canvas to blob
return new Promise(resolve => {
canvas.toBlob(resolve, 'image/png')
})
}
// 4. Upload blob to Sanity
async function uploadOgImage(blob, documentId) {
const asset = await client.assets.upload('image', blob, {
filename: `og-image-${documentId}.png`,
contentType: 'image/png'
})
// 5. Attach to your document
await client
.patch(documentId)
.set({
ogImage: {
_type: 'image',
asset: {
_type: 'reference',
_ref: asset._id
}
}
})
.commit()
return asset
}
// Put it all together
async function handleGenerateOgImage(documentId, title, description) {
const blob = await generateOgImage(title, description)
const asset = await uploadOgImage(blob, documentId)
// 6. Use the URL in your og:image tag
const imageUrl = asset.url
console.log('og:image URL:', imageUrl)
return imageUrl
}Using with Custom Asset Sources
Since you mentioned looking at custom asset sources, you could also create a custom asset source plugin that integrates this generation directly into your Studio's image field. This would give your editors a "Generate og:image" button right in the image input.
Here's a simplified example of a custom asset source:
// customOgImageSource.js
export default {
name: 'og-image-generator',
title: 'Generate OG Image',
component: OgImageGenerator,
icon: () => '🎨'
}
function OgImageGenerator({onSelect, document}) {
const handleGenerate = async () => {
const blob = await generateOgImage(document.title, document.description)
// Upload using the client
const asset = await client.assets.upload('image', blob, {
filename: `og-${document._id}.png`
})
// Return to the image field
onSelect([{
kind: 'assetDocumentId',
value: asset._id
}])
}
return <button onClick={handleGenerate}>Generate OG Image</button>
}Then configure it in your schema:
{
name: 'ogImage',
type: 'image',
options: {
sources: [
{name: 'og-image-generator', title: 'Generate', component: OgImageGenerator}
]
}
}Key Points
- The
client.assets.upload()method accepts blobs directly in the browser - You need a write token to upload assets
- The uploaded asset gets a permanent URL on Sanity's CDN that you can use in your
og:imagemeta tags - The asset is stored as a proper Sanity asset document and can be referenced from your content documents
This approach gives you a fully automated og:image generation workflow that stores the images properly in Sanity's asset system!
Show original thread5 replies
Was this answer helpful?
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.