
Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag storeYes, 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.
You can create a custom button or action that:
client.assets.upload() to upload it to Sanity's CDNHere'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>
)
}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]
}
}Based on the custom asset sources documentation:
kind: 'file': When using onSelect in a custom asset source, use kind: '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{_type: 'image', asset: {_type: 'reference', _ref: assetId}}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 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.
Content operations
Content backend


The only platform powering content operations
By Industry


Tecovas strengthens their customer connections
Build and Share

Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag store