Migrate from Sanity to Payload: what the move involves
The move from Sanity to Payload is mainly a schema rewrite plus a data load. Your Sanity Studio schemas are defined in code, which maps well to Payload, but Payload collections, fields and access rules have to be written again in its own TypeScript configuration, and the documents in Content Lake have to be loaded into the database you run. Nothing about the content model transfers automatically, so plan the work as two streams that depend on each other: the schema work comes first, and the data load is only as reliable as the collections it targets.
Platform or front-end engineers usually do the technical work, with content owners reviewing field names, draft behavior and editorial workflows. Payload is MIT licensed and installs directly into an existing Next.js /app folder, so the team that already maintains a Next.js front end often owns the CMS as well. The effort depends on three factors you can measure before you start: the number of document types and their references, the volume and shape of Portable Text fields, and how many assets sit in the dataset and whether they are still reachable. Ask for an inventory of each before estimating the work.
Before committing, confirm three things with your own environment and the export you plan to take. First, check which Sanity assets the sanity dataset export run skips, because skipped files must be recovered from their source. Second, decide which Payload database you will run, since Payload supports MongoDB, PostgreSQL and SQLite, and that choice shapes hosting and backup. Third, review how Payload's versions and drafts map to the history you need to keep, because Sanity document history in the export does not become Payload version history automatically. Current Payload release details are in the Payload GitHub releases page, so pin a version for the migration and confirm its behavior in a test environment before loading production content.
Which Sanity capabilities map cleanly to Payload
This table maps each main Sanity capability to its closest Payload equivalent and marks it as clean, needs rework or gap. Read the status as a planning signal: clean means a direct concept exists, needs rework means the data or code must be reshaped, and gap means your team must build or accept a different approach. Treat every row as a point to confirm in your own environment before you commit to a schema plan.
| Sanity capability | Payload equivalent | Status and what to confirm |
|---|---|---|
| Studio schemas as code | Collections and fields defined in TypeScript config | Clean. Both are code-defined, but Payload schemas live in the Next.js app, so the Studio schema files are rewritten, not copied. |
| Document types | Collections, with globals for single-instance content | Clean. Singleton documents such as site settings usually become globals. Confirm which types need that treatment. |
| References between documents | Relationship and upload fields | Needs rework. Check reference depth, reverse lookups and any weak or cross-dataset references, which need explicit handling. |
| Portable Text rich text | Lexical rich text editor with custom blocks | Needs rework. Convert block content to Lexical structure and retest custom marks, annotations and embedded objects. |
| Image and file assets | Upload collections stored in local disk or object storage | Needs rework. Assets must be re-downloaded from the export and re-uploaded. Confirm how skipped assets (401, 403 or 404 responses) are handled. |
| Drafts and published content | Versions and drafts | Clean for drafts. Confirm how many versions you keep and whether older Sanity history must be imported or only the latest state. |
| Document history | Versions table in the database | Needs rework or gap. Sanity history from the export is not automatically replayed as Payload versions. Confirm what depth your compliance or audit needs require. |
| Real-time collaboration | Admin panel with live preview, without Sanity's multi-user live editing model | Gap. Confirm with the vendor and in a test environment how concurrent editing and conflicts behave before you rely on it. |
| Access control | Field-level and document-level access functions | Needs rework. Sanity roles and dataset permissions must be rebuilt as Payload access rules and user collections. |
| Hooks and automation | Document and field hooks | Clean. Replace Sanity webhooks and functions with Payload hooks and your own job runner, and test each trigger. |
| Hosting and database | Self-hosted PostgreSQL or MongoDB, with the app on your infrastructure | Needs rework. Your team takes on database backups, upgrades and scaling that Content Lake handled. Confirm ownership and recovery targets. |
What to export from Sanity and what to rebuild in Payload
The Sanity export is a documented starting point, but it carries documents and files, not the surrounding setup. Each card below separates what the tarball gives you from what your team must recreate in Payload and what to confirm before cutover.
The dataset export tarball
The sanity dataset export command writes a local gzipped tarball that includes document history and asset files. This is the only complete package of content you will receive, so store it with a checksum and keep a copy outside the migration workstation. Run the export against each dataset you plan to move, including any staging or preview datasets that hold content editors still use.
Assets skipped during export
The export skips any asset that returns a 401, 403 or 404 response. Those files will not appear in the tarball, so a document may point to an image that is missing from your new upload collection. Compare the asset references in exported documents against the files actually present, and list every gap with its owning editor before you retire the source dataset.
Rebuilding the upload collections
Assets held in Content Lake must be re-uploaded into Payload upload collections and stored in your own object storage or file system. Map each Sanity image and file field to an upload relationship, and keep alt text and captions with the file record. Confirm how your chosen storage adapter handles image sizes and derived files before you load the full library.
Webhooks and GROQ queries
Sanity webhooks, GROQ queries and the front-end code that calls them are not part of the export. Each GROQ query needs a Payload equivalent, usually a REST or local API call with filters that match the same fields. Inventory every webhook target and every query in your front end, then decide which ones point at Payload and which were only needed for the Content Lake.
Preview and draft setup
Payload's versions and drafts and live preview must be configured for each collection that editors use. The export carries history, but the preview URLs, draft-to-published rules and Studio-side preview wiring must be recreated by your team. Test a draft from start to finish with an editor account before you direct production traffic to the new site.
Users, roles and access to confirm
The export documentation covers content, not who can edit it. Ask Sanity for documented guidance on members, roles and API tokens, then rebuild equivalent users and field-level access rules in Payload's built-in authentication. Confirm with your own records who must keep access during cutover, because role data may not travel with the content.
Migration sequence from inventory to decommission
The sequence moves a Sanity project to Payload in controlled stages, so each schema, dataset and delivery path is proven on a copy before editors and consumers depend on it. Every step ends with a written artifact that the next step relies on, and the Sanity dataset is only decommissioned after reconciliation and a tested rollback path.
Inventory schemas, datasets and consumers
Your engineering lead and content lead list every document type in the Sanity Studio schema, every dataset and environment, the GROQ queries and webhooks the front ends depend on, and the editors who use the Studio daily. Record which document types are published, which are drafts only, and which fields hold Portable Text or asset references. The output is a schema inventory and a consumer list. You know this step is done when every front end and integration that reads Content Lake has a named owner and a documented query.
Export the dataset and map the schema
Run the Sanity CLI dataset export to produce the gzipped tarball with documents, history and asset files, and note the assets it skips on 401, 403 or 404 responses. Your team then drafts a schema map that pairs each Sanity document type with a Payload collection or global, and each reference with a relationship field. Decisions about Portable Text conversion to Lexical and about which history to keep are recorded here. The output is a reviewed schema map and a list of skipped assets to resolve. This step is done when the map has been signed off by the content lead.
Build the Payload pilot on a copy
Your developers install Payload into a Next.js app, define collections from the schema map, and enable versions and drafts where the Sanity documents used them. They load a copy of a small set of document types from the export, using a script that reads the NDJSON documents and writes them through the Payload local API, then re-attach assets to upload collections. The output is a loaded pilot dataset on a non-production database. The step is done when the pilot collections render the same fields as the Studio for a representative sample of documents.
Run the full load and reconcile
Extend the loader to the full dataset and run it against a staging instance that uses the database you will operate in production, such as PostgreSQL or MongoDB. Compare document counts, reference integrity, locale coverage, published versus draft status and asset checksums against the export manifest. Editors review a sample of converted rich text. The output is a reconciliation report listing every mismatch, its cause and its fix. The step is done when the report shows no unexplained differences and the content lead accepts the exceptions in writing.
Run Studio and Payload in parallel
Keep Sanity Studio and Content Lake as the system of record while editors work in both tools for an agreed period. Set up a repeatable delta load from the Sanity side into Payload, and have the delivery team point a staging front end at Payload's API. Track where content is written, which queries return different results and how long delta loads take. The output is a parallel-run log and a list of open differences. This step is done when the front ends render identically from both sources for the page types in scope.
Cut over delivery and editing
Agree a cutover window with editors and the owners of each consumer. Freeze Studio writes, run the final delta load, reconcile again, then switch production front ends and integrations to Payload and move editors to the Payload admin panel. Keep the Sanity dataset read-only rather than deleting it. The output is a cutover checklist with sign-off from each owner and a documented rollback plan that names the steps to point consumers back to Sanity. The step is done when production traffic and editing both run from Payload and the rollback plan has been rehearsed.
Decommission the Sanity dataset
After a period the content lead and engineering lead agree on, archive the final Sanity export with its checksums in storage you control, revoke tokens and webhooks, and remove the Sanity project configuration from deployments. Confirm with Sanity how your plan and data retention terms apply to the dataset before you delete it, and ask for any contractual exit obligations in writing. The output is an archive record and a decommission certificate signed internally. The step is done when no service reads from Content Lake and the archive can be restored into a test instance.
How to run Sanity and Payload in parallel and cut over
The freeze-and-load approach stops editing in Sanity for a defined window, takes a full dataset export, loads it into Payload, verifies the result and then opens Payload for editors. It is the simplest model to reason about because the export is a single consistent snapshot, and no change has to be merged after the fact. The cost is editor downtime, which grows with the amount of work that must be verified before the switch. Because the export tarball carries document history along with the documents, the load step must decide what to do with drafts and published versions in Payload's versions and drafts model before anyone edits.
- Editor instructions: Editors publish or discard pending drafts before the freeze, and they stop creating content in Studio once the window opens.
- Export point: Run the
sanity dataset exportcommand after the freeze begins so the snapshot contains no late edits. - Draft handling: Load published documents as the live version and load unpublished drafts as Payload drafts, then confirm each one in the admin panel.
- Asset check: Compare the asset files in the tarball against the documents that reference them, since the export skips assets that return 401, 403 or 404 responses.
- Rollback path: Keep Sanity read-only until the Payload site has passed content review.
Confirm with your editorial leads how long the freeze can run before publishing schedules are affected, and confirm the rollback steps with the team that owns the Sanity dataset.
The delta-sync approach keeps Sanity open for editing while you load a baseline export into Payload, then replays only the documents changed after the export point. Editors keep working with little interruption, and the window for disruption is reduced to the final cutover. The trade-off is engineering work: the team must identify changed documents reliably, map each change through the same schema translation used for the baseline, and handle deletions and conflicting edits. Drafts need extra attention because a document can be edited in Sanity during the sync while a Payload draft already exists for it.
- Change detection: Record the export timestamp and select documents with later revisions for the replay, then test that deletions are captured separately.
- Conflict rule: Decide in advance whether Sanity or Payload wins when the same document changed on both sides, and document that rule for editors.
- Draft reconciliation: Replay draft changes into Payload drafts rather than published versions, so approvals still happen in the Payload workflow.
- Repeat runs: Run the delta several times before the final cutover, and check that each run is idempotent.
- Cutover signal: Announce a short final freeze, run the last delta, and switch the public site once counts and sample checks match.
Confirm with your Sanity administrator how revision data is exposed in the export so the delta logic matches what the dataset actually records.
The content-by-content approach moves one content group at a time, such as a section of the site or a single document type, and leaves the rest in Sanity until its turn. It suits smaller sites or sites with clearly separate content areas, and it lets the team learn the schema mapping on low-risk content first. Each group runs its own cutover, so editors work in two systems for a period. That split needs clear routing: the public site must know which source serves which page, and cross-references between groups must resolve correctly on both sides.
- Group selection: Start with content that has few references to other document types and a small editor audience.
- Routing map: Document which front-end routes read from Sanity and which read from Payload, and update that map with each group.
- Reference handling: Where a Payload document points to a Sanity document not yet moved, decide whether to store a temporary link or wait for the target group.
- Draft handling: Move drafts with their group, so an editor never has pending work in a system that no longer serves that content.
- Permissions: Set Payload access control for each group before its editors are given access, since Payload field-level access is configured in code.
Confirm with your editorial and engineering leads how many groups the site will carry and whether the split still makes sense once cross-group references are counted.
What drives migration cost and duration
This table lists the factors that change the effort of moving a Sanity project to Payload, why each one matters, and how to keep it contained. Read it before requesting a quote, because the drivers below determine most of the scope. Costs fall into one-time migration work and recurring hosting and maintenance, and the last rows show what to ask for in writing.
| Driver | Why it changes effort | How to keep it down |
|---|---|---|
| Document types and references | Each Studio schema becomes a Payload collection or global with its own fields, and every reference field must map to a relationship with matching IDs. | Trim unused document types before export and consolidate near-duplicate schemas into one collection with a type field. |
| Portable Text with custom blocks | Custom Portable Text blocks, marks and annotations must be converted to Lexical nodes or Payload blocks, which is often the most manual conversion step. | Inventory custom block types early and keep the block set small. Convert a sample of real articles first to test the mapping. |
| Asset count and size | Assets in the export tarball must be re-uploaded to your storage, and skipped assets returning 401, 403 or 404 must be recovered or replaced by editors. | Confirm asset access before export, list skipped files from the export output, and decide early whether missing assets are restored or dropped. |
| Preserved history and drafts | Keeping full document history means mapping Sanity revisions into Payload versions and drafts, which requires custom scripts and validation. | Decide whether history is archival only or needs to be live in the CMS. Preserving only published versions and drafts usually narrows the work. |
| Number of locales | Each locale multiplies the fields to translate, the localized content to verify and the routing rules in the front end. | Confirm which locales are live today and migrate those first. Retire inactive locales before the move. |
| Custom GROQ queries | GROQ queries in the front end must be rewritten as Payload Local API or REST calls, and each query needs a comparison test against the original output. | Inventory all GROQ queries in the codebase and group them by page template to reduce the number of distinct rewrites. |
| Front-end rebuild scope | If the site is rendered from Sanity data today, moving to Payload also means rebuilding routing, rendering and preview in a Next.js application. | Separate the content migration from any front-end redesign so each can be tested and released on its own. |
| Hosting setup | Self-hosting Payload requires a chosen database (MongoDB, PostgreSQL or SQLite), object storage, a CDN and a Next.js deployment, each with its own configuration and backup plan. | State the target cloud, database and storage products in the scope so the quote covers provisioning, backups and monitoring. |
| One-time versus recurring | One-time work includes schema rebuild, data transformation, asset transfer, testing and cutover. Recurring costs include hosting, database and storage, backups, security patching, Payload upgrades and support. | Ask the quote to separate these two groups, and ask which items are fixed scope and which are retainers. |
| Items to request in a written quote | Ask for a list of document types, locales, custom blocks and GROQ queries in scope, the export and asset recovery plan, the test and cutover criteria, and the hosting and backup responsibilities. | Compare quotes against the same inventory so that each line can be checked against the actual project. |
What usually goes wrong and how to prevent it
Most failed Sanity to Payload migrations fail at the edges: assets that never arrived, drafts that lost their state, and content that two systems changed at once. Each card below names one of these failure points and the check that catches it before editors notice.
Assets skipped on 401, 403 or 404
The Sanity dataset export skips any asset that returns 401, 403 or 404, so the tarball can be complete while image and file references are not. Editors then see broken media in Payload with no error at import time. Before cutover, compare the asset count in the export manifest with the files you actually uploaded, and list every skipped reference so each one is fixed or accepted in writing.
Draft and history state lost
Payload's versions and drafts are a different model from Sanity's document history and draft IDs. A naive import writes only the latest published body, so editors lose unpublished work and the revision trail that auditors may ask for. Decide which states must survive, such as in-progress drafts or the last published version, and verify a sample of each in Payload's admin panel before the migration is signed off.
Portable Text converted without custom blocks
Converting Portable Text to Lexical works for standard marks and headings but often fails on custom marks, annotations and object blocks defined in your Studio schema. Those fields may render as empty paragraphs or disappear without an error. Build a fixture set of the most complex documents from your schema, run the converter against it, and review the rendered output in both systems for each custom type.
Broken references and slugs
Sanity references are IDs, and many sites link to pages by slug. If slugs change during the move, or reference fields are mapped to the wrong Payload collection, internal links and page relationships break silently. Generate a map of old IDs to new IDs and old slugs to new paths, keep the old URLs as redirects, and crawl the migrated site to confirm that every internal link resolves.
Editors working in both systems
During a parallel run, an editor who updates a page in Sanity while the import is in progress creates a conflict that the one-time import cannot resolve. Payload then holds an outdated copy that looks current. Pick a single cutover date, freeze publishing in Sanity for the final import window, and run a delta export to capture changes made after the first load.
Treating Studio's MIT license as covering content
Sanity Studio is MIT licensed, but the content it edits lives in Content Lake, which Sanity hosts. Open-sourcing the Studio code does not give you a copy of your content or a right to keep editing it in a hosted service you no longer pay for. Confirm your export covers every dataset you use, store the tarball under your own retention policy, and check that no document type depends on a hosted Sanity function that Payload cannot reproduce.
How the migration differs by starting point
A small team with a handful of document types and one public site usually has the smallest migration. The schemas fit in one Payload config, the dataset is a single export, and the main work is rebuilding the front end against Payload's generated types. Because Payload installs into a Next.js /app folder, the team can often keep one codebase for the Studio-equivalent admin and the site.
- Schema scope: Count the document types, references and Portable Text fields, then map each to a Payload collection, global or block.
- Front end scope: Confirm whether the existing GROQ queries and image pipeline are rewritten or replaced, since this is often larger than the content move.
- Drafts and history: Decide whether editors need past versions in Payload or only the current published and draft states.
- Hosting: Confirm where the Next.js app and database will run, and who holds the backups.
- Editors: Plan a short walkthrough of the new admin panel before the old Studio is switched off.
For a single-site estate, check that the export tarball holds every asset the live site renders before the hosted dataset is retired.
An estate with several sites, many datasets or several locales needs a mapping exercise before any data moves. Each dataset may hold a different schema version, and shared document types can be used differently by each brand. Payload's localization and versions features cover much of this, but the team must decide how each dataset maps to one collection set, one tenant or separate installations.
- Dataset inventory: List each dataset, its schema version and which sites read from it, then decide which ones merge.
- Locale mapping: Confirm how Sanity's language fields and translation documents map to Payload's localized fields and locale codes.
- Cross-dataset references: Identify any references or shared assets that point across datasets, because these need new IDs.
- Import order: Load shared taxonomies and assets before the entries that reference them, and run the import once per dataset in a staging copy.
- Parity checks: Confirm that each locale renders the same entries, slugs and redirects as before.
For a multi-locale estate, ask whether the team wants one Payload instance for all sites or separate instances, because that choice drives the database and deployment plan.
A regulated team, such as one in banking, healthcare or the public sector, usually needs to show where content, assets, drafts and backups are stored. Moving content out of Content Lake into a database the team controls changes that picture entirely, but it also moves the team's obligations to its own infrastructure. Sanity Studio being MIT licensed does not, on its own, move the content, so the residency question has to be answered for the data itself.
- Data location: Confirm the region and provider for the database, object storage, backups and the export tarball during the migration.
- Access control: Map who can read drafts and history in Sanity today to Payload's field-level access rules and admin roles.
- Export handling: Confirm how the dataset export is stored, encrypted and deleted after import, and who approves that step.
- Evidence: Record the import logs, the skipped assets list and the reconciliation results as part of the change record.
- Assessor input: Ask the internal audit or compliance team which controls they expect to see in evidence before the cutover date is set.
DeployOpen prepares systems, controls and evidence for assessment, but the audit conclusion comes from the independent assessor the organization engages.
Common questions before moving from Sanity to Payload
How much effort does a move from Sanity to Payload take?
The effort depends mainly on how many document types, references and custom Studio components you have, how much Portable Text sits in your content, and how many assets must be re-uploaded. Each Sanity schema must be rewritten as a Payload collection or global with its fields, validations and access rules, so a Studio with many custom input components takes longer than one built on standard fields. Ask any implementer to size the work from an inventory of your schemas, your dataset export and your front-end queries, and to split the effort into schema rewrite, content load, asset handling, front-end changes and cutover.
Can our in-house team do the migration alone?
A team that already writes TypeScript and runs Next.js can do the work in-house, provided someone owns the schema mapping and the content load. The skills involved are Payload configuration, a PostgreSQL or MongoDB database, a Next.js front end that reads Payload through its local API or REST and GraphQL endpoints, and scripting that reads the NDJSON documents from the export. Teams without that experience often underestimate the reference mapping and Portable Text conversion. A fixed-scope engagement from DeployOpen can cover planning, implementation, testing and documentation, with the written scope agreed before work starts.
What do we need to provide for the migration?
You need to provide access to the Sanity project and its Studio code, a full dataset export, the front-end code that queries Sanity with GROQ, and the people who know the content model and editorial workflow. You also need the hosting accounts you plan to use for the Next.js app, the database and object storage, and a named decision-maker who approves the content mapping. Ask the implementer to list these inputs in the written scope so gaps surface before work begins, not during cutover.
Do drafts and document history carry over from Sanity to Payload?
Payload has built-in versions and drafts, so published and draft states can be represented, but history does not transfer automatically from the Sanity export. The sanity dataset export command writes a gzipped tarball that includes document history and asset files, so the history exists, yet it is stored in Sanity's format and must be mapped or loaded deliberately. Decide early whether you need full revision history in Payload, only the current published and draft versions, or an archive of the old history kept in read-only form. Each choice changes the load script and the effort.
Does Portable Text convert cleanly to Payload's Lexical editor?
Portable Text is structured JSON, and Payload's rich text editor is Lexical, so conversion is possible but is not a one-click step. Custom marks, inline objects and custom block types in your Studio schemas need explicit mapping rules, and any content that relies on those types needs a test pass with real editors' content. Plan a converter, run it against a copy of the export, and review the output in the Payload admin panel before you commit to a cutover date. Expect a short list of blocks that need manual rework.
What does hosting Payload require, and where can it run?
Payload is a Next.js native CMS that installs into an existing Next.js /app folder, and it supports MongoDB, PostgreSQL and SQLite. The project's documentation describes deployment to Vercel, to Cloudflare Workers with R2 and D1, or self-hosting anywhere you run Node.js. Your recurring costs are the compute running the Next.js app, the database, object storage for assets, backups and a CDN if you use one. Ask any provider to quote each of these separately, and confirm which database and storage you will run, because that choice drives the operating model.
Is Payload licensed in a way that allows us to run it ourselves?
Payload is MIT licensed, which permits you to use, modify and deploy it inside your own infrastructure without a per-seat or per-request fee from the project. The license covers the CMS code, but it does not cover your own Sanity content or any third-party packages you add, so review the license of each dependency in your build. Confirm the license terms with your legal team against the current repository before signing, since release lines such as the v4.0.0 beta can change packaging and defaults.
How DeployOpen scopes and runs a Sanity to Payload migration
DeployOpen plans, implements, integrates, hardens, backs up, documents and supports self-hosted open-source platforms in your own infrastructure, including Payload. For a Sanity to Payload move, we start from a written, fixed scope that names the schemas, datasets, assets and environments involved before any migration work begins.
How we scope
- Inventory every Sanity Studio schema, document type, reference and plugin that the Payload collections must replace.
- Confirm which datasets, draft states and history records must move, and which can be archived as the export tarball.
- Agree in writing where Payload will run, which database (PostgreSQL, MongoDB or SQLite) it will use, and who owns each environment.
How we build
- Rewrite Studio schemas as Payload collections and globals, with generated TypeScript types checked against the front-end code.
- Build conversion scripts that map Portable Text to Lexical, then test rendered output on representative pages before loading.
- Load documents and assets from the export tarball into a staging instance, verifying counts, references and skipped files.
How we hand over
- Deliver the collection mapping, conversion scripts, load logs and a list of assets that the export skipped or that need manual review.
- Document how drafts, versions and localized content behave in Payload, so editors and developers know what changed.
- Walk through backup, upgrade and rollback procedures for the self-hosted Payload and database before cutover.
How we support
- Support after cutover is available as a retainer, covering Payload upgrades, security patches and backup checks.
- Ask for a retainer scope that states response expectations, the Payload versions covered and who approves changes.
- Use continuous security testing, if included in scope, to review the Payload application and its access control rules after launch.

