Snabbsite · Docs
For website ownersFor developers
Site Kit SDK

Import Sanity

Turn a Sanity dataset export into Snabbsite collections and pages, with a mapping you review and commit. Nothing from the dataset or the schema is executed.

Your client's content already lives in Sanity, and rebuilding twenty properties by hand is not a migration anybody actually performs. This lane moves the content you already have.

It needs two inputs, and the second one is not optional in practice:

  1. A dataset export, produced by sanity dataset export. It is NDJSON plus an images/ and files/ folder in a tarball.
  2. Your schema directory, the defineType calls in your own repository. The export says a document has a field called body. Only the schema says that body is Portable Text with three custom block types in it.

Your schema files are read as text. They are never imported and never run, so a schema that imports your own React components is still safe to point at. No GROQ is evaluated at any point.

Two steps, and the stop in the middle is the point

The mapping decides what a client's whole content history becomes. Getting it wrong quietly is the failure worth designing against, so the tool proposes and a person confirms.

1. Propose

npx @snabbsajt/cli site import sanity \
  --export ./production.tar.gz \
  --schema ./sanity/schemas \
  --propose

That writes sanity-mapping.json and stops. Nothing is converted yet.

Every document type is proposed as a collection, a page, or skip. Every field is proposed as one of the nine collection field types, or as skip with a note saying why. A field we could not place is present and skipped, never absent. A missing entry and an unnoticed field look the same in a file, and that is how content disappears without anybody being told.

2. Review, commit, convert

Open the file. Fix what is wrong. Commit it: from here the conversion is deterministic and re-runnable, which is what lets you iterate on a migration.

npx @snabbsajt/cli site import sanity \
  --export ./production.tar.gz \
  --schema ./sanity/schemas \
  --mapping ./sanity-mapping.json \
  --name "Client AB" \
  --out ./client-snabbsite

npx @snabbsajt/cli site import approve ./client-snabbsite --yes

Then push the package the way you push any other one.

Running it again is the normal case

Import, look at the result, fix the mapping, import again. A row is matched on its Sanity _id, so a second run updates the row instead of writing a second copy of the same property.

What happens to a row on the second run:

  • Nobody touched it in Snabbsite: updated in place.
  • The client edited it in Snabbsite: left exactly as it is, and reported as a conflict. Your push never overwrites their words.
  • It is new: added.
  • The export stopped carrying it: nothing. Rows are never deleted.

A list's shape follows the same idea: a new optional field is added, and a field that would be removed, retyped, or added as required is held and named. Any of those would break rows the client already wrote.

What comes across, and what is reported instead

  • Rich text becomes plain text. A collection field holds text, and our text fields carry no HTML. Links come across as text (https://…). Bold and italic are dropped. Every embedded block is listed in the report by name: a picture inside the text, a callout, a YouTube embed, one of your own components. Nothing is invented in its place.
  • A document mapped to a page arrives empty, with its title and its address. Its text is in the report. A Sanity document is data; guessing a hero and three bands out of it would be a worse result than an honest blank page.
  • Hotspot and crop are reported, not approximated. The full picture comes across. Set the focal point again in Snabbsite if a crop looks wrong.
  • Drafts come across hidden. A drafts. document with no published twin is imported and excluded from publishing, so nothing unfinished goes live.
  • One language per hemsida. We detect which convention your dataset uses: per-field objects, one document per language, or the document-internationalization plugin. A dataset where two of them look equally likely stops the run and says what it found, because picking one would drop the other's content without telling you. Import the second language as its own hemsida.

More than 200 pictures

One import accepts 200 assets, and a bundle carrying 201 is rejected whole. A portfolio past that is split for you into run-01, run-02 and so on: import the first, then merge the rest into the same hemsida. Each row travels with its own pictures, so no run ever points at an image it does not carry.

Never executed or imported as runtime

GROQ queries, your Studio schema files, Portable Text serializers, custom React block components, Studio structure, desk configuration and workflows. None of it runs, and none of it is imported as code.

This is a migration, not an integration: there is no live sync with Sanity, no read-through to their API, and nothing is ever written back.


Didn't find the answer, or is something wrong here? Tell us.

Last updated on

On this page