Joint session with Vercel: How to build intelligent storefronts (May 15th)

Migrating from default image type to custom image object without re-uploading

1 replyLast updated: Nov 30, 2025

I have some objects with images, but i figured i should have made a custom image object, which supports things like alt text. Is there a way to not having to re add the images for every object/document where i want to replace
type: 'image'
with
type: 'customImage'
?
AI Update

Good news! You don't have to re-add all your images. You can migrate your existing image data to use the new custom image type through a migration script.

Since Sanity's backend is schemaless, your existing image data is still there even after you change the schema. The key is to write a migration script that transforms your existing documents to add the new fields (like alt text) while preserving the existing image references.

Here's the general approach:

Here's an example migration script:

import {getCliClient} from 'sanity/cli'

const client = getCliClient()

const fetchDocuments = () => 
  client.fetch(`*[_type == "yourDocType" && defined(yourImageField._ref)]`)

const buildPatches = (docs) =>
  docs.map((doc) => ({
    id: doc._id,
    patch: {
      set: {
        yourImageField: {
          _type: 'customImage',
          asset: {
            _type: 'reference',
            _ref: doc.yourImageField._ref // Preserve existing image reference
          },
          alt: '' // Add default alt text, or derive from existing data
        }
      }
    }
  }))

const createTransaction = (patches) =>
  patches.reduce(
    (tx, patch) => tx.patch(patch.id, patch.patch),
    client.transaction()
  )

const migrateNextBatch = async () => {
  const documents = await fetchDocuments()
  const patches = buildPatches(documents)
  if (patches.length === 0) {
    console.log('No more documents to migrate!')
    return null
  }
  console.log(`Migrating batch:\n %s`, patches.map((patch) => patch.id).join('\n'))
  const transaction = createTransaction(patches)
  await transaction.commit()
  return migrateNextBatch()
}

migrateNextBatch().catch((err) => {
  console.error(err)
  process.exit(1)
})

Key principles from Sanity's migration best practices:

You can also use the deprecated field feature to mark old fields during the transition period, so they're visible on documents that still have them but hidden on new documents.

For more complex migrations, check out the schema and content migrations documentation and the handling schema changes course.

Show original thread
1 reply

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.

Related contributions