# Validate your import in Studio

https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/validate-import-studio

Check that references resolve, rich text renders, and assets load by inspecting imported documents in Sanity Studio.

---

## Navigation

**Course:** [Migrating from Webflow to Sanity](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity) · [View as markdown](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity.md)

**Previous:** [Import content with the two-pass pattern](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/two-pass-import) · **Next:** [Set up forms, redirects, and sitemap](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/forms-redirects-sitemap)

---

## Course Contents

1. [Introduction](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/introduction-to-webflow-migration)
2. [Audit your Webflow content](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/audit-webflow-content)
3. [Recognize Webflow's content modeling patterns](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/recognize-content-modeling-patterns)
4. [Export from Webflow and connect the Data API](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/export-webflow-connect-api)
5. [Initialize Sanity and install the migration toolkit](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/initialize-sanity-migration-toolkit)
6. [Map collections to Sanity document types](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/map-collections-to-schemas)
7. [Build a page builder with a sections array](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/page-builder-sections-array)
8. [Model custom-template pages as singletons](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/custom-template-singletons)
9. [Model navigation in Sanity](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/model-navigation)
10. [Create a siteSettings singleton](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/site-settings-singleton)
11. [Handle internal links in Portable Text](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/internal-links-portable-text)
12. [Fetch your collection items from the API](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/fetch-collection-items-api)
13. [Run the asset pre-upload pass](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/asset-pre-upload)
14. [Resolve reference fields from API data](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/resolve-reference-fields)
15. [Convert rich text to Portable Text](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/convert-rich-text-portable-text)
16. [Extract static page content using the page DOM API](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/extract-static-page-content)
17. [Import content with the two-pass pattern](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/two-pass-import)
18. **Validate your import in Studio** *(current)*
19. [Set up forms, redirects, and sitemap](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/forms-redirects-sitemap)
20. [Set up draft mode and visual preview](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/draft-mode-visual-preview)
21. [Validate your import without a frontend](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/validate-migration)
22. [Launch checklist and DNS transfer](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/launch-checklist-dns)
23. [AI prompt library and resources](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity/appendix-prompt-library-resources)

---

After running import scripts against the staging dataset, open Studio at localhost:3333 and check these before moving to production:



**Reference fields** — each reference should show the referenced document's title, not a broken icon. If broken, check id-map.json. The Webflow item ID may not have been written during pass 1.



**Rich text** — images should render, internal links should resolve. If images are missing, check asset-map.json against the original Webflow CDN URL.



**HTML embeds** — if your content had w-embed blocks, verify the htmlEmbed field contains the expected HTML string.



**Slugs** — confirm slugs match the `fieldData.slug` values from your API export. Import scripts set `slug.current` directly from `fieldData.slug`, so slugs should match exactly regardless of whether `source` is present on the schema.



### Checking for leftover _weak: true references



After running resolve-refs, verify no placeholder weak references remain:



```javascript
// Quick GROQ check — should return an empty array
await client.fetch(`*[_type in ["blog","htmlTag","cssProperty"]][0...5]{
  _id,
  "weakAuthor": author._weak,
  "weakTags": tags[]._weak
}`)
```

If you find remaining `_weak: true` values on array reference fields (like tags), the patch approach matters. `patch.unset(['tags[0]._weak'])` doesn't work reliably on array sub-fields in Sanity's patch API. The correct approach is to fetch the array, remove `_weak` from each item in JavaScript, and replace the whole array with `patch.set({ tags: cleanedArray })`:



```javascript
const cleanedTags = doc.tags.map(({ _weak, ...rest }) => rest)
await client.patch(doc._id).set({ tags: cleanedTags }).commit()
```

When all checks pass on a representative sample (10 to 20 documents per collection), run the full import for that collection. Repeat before cutover.



### Deriving CMS SEO from detail page templates



After first import, CMS documents often have empty or thin `seo` objects. This is expected: Webflow doesn't put SEO metadata directly in fieldData for CMS items. It lives in the collection's detail page template, configured in Webflow Designer using interpolation syntax.



To find it: open `webflow/api/pages.json` and filter for pages where `collectionId` matches your collection and the slug starts with `detail_` (e.g. `detail_blog`, `detail_faq`). Each template page has a `seo` object with fields like:



```json
{
  "title": "{{wf {path:name} }} — My Site",
  "description": "{{wf {path:snippet} }}",
  "openGraph": {
    "title": "{{wf {path:name} }}",
    "descriptionOverride": "{{wf {path:snippet} }}",
    "image": "{{wf {path:og-image} }}"
  }
}
```

The `{{wf {path:fieldName} }}` tokens are Webflow's interpolation syntax — each refers to a field in the collection's fieldData. Read the template to understand which fields map to which SEO properties, then write a per-collection transform helper:



```javascript
// Blog posts: name → metaTitle, snippet → metaDescription, og-image → ogImage
function seoFromBlogItem(item) {
  return {
    metaTitle: item.name,
    metaDescription: item.snippet ?? '',
    ogImage: item['og-image'] ? imageField(item['og-image']) : null,
    noIndex: false,
  }
}
```

Some templates reference related collection fields indirectly — for example, an FAQ's OG image might come from a referenced tag item's `og-image` field rather than the FAQ item itself. When you see `{{wf {path:someReference.fieldName} }}`, you'll need a lookup: resolve the reference ID from fieldData, find that item in the related collection's NDJSON, and pull the field from there.



After updating your transform functions, re-import the affected collections and validate:



```groq
// GROQ check — should return 0 for a complete import
count(*[_type == "blog" && !defined(seo.metaTitle)])
```

**Important distinction: **Content Agent is useful for backfilling generated content (alt text, missing descriptions where no source data exists). Structural SEO, title patterns tied to URL templates, should be derived at import time from the detail template, not left for AI to generate. If the template says `name` → `metaTitle`, that's an exact mapping, not a creative writing task.



### Expect to iterate



A first import that passes all the checks above is a good first import, it isn't a finished import. Treat the validate step as an invitation to iterate, not a pass/fail gate. Practical things that commonly need a second pass:



- **Rich text patterns you missed**: your content probably has at least one HTML pattern the first version of rich-text.js didn't handle. When you find one, fix the rule, re-import that collection, re-run resolve-refs, and check again. Because the script uses createOrReplace, re-running is safe.

- **SEO and metadata gaps**: Webflow's API often exports sparse SEO data — missing meta descriptions, thin image alt text, blank OG images where no template exists. Run [Sanity Content Agent](https://www.sanity.io/docs/content-agent) against your staging dataset before launch to backfill these fields at scale rather than editing them individually. Use Content Agent for generated copy; use the detail template approach above for structured field mappings.

- **Internal links still showing as external**: if you ran the import before generating url-map.json (or before all collections were imported), a subset of internal links will be stored as plain external URLs. Running the internalLink conversion pass described in the internal links lesson fixes this without re-importing.

- **Field values that need editorial cleanup**: some content in Webflow was formatted for Webflow's renderer — inline styles, Webflow-specific class names embedded in rich text, or content that only makes sense in a Webflow layout. These need human review, not a script fix. Build time for that into your pre-launch timeline.

- **Visual QA**: scripts can verify field values but not layout. Load a sample of migrated pages in your frontend and compare them against the Webflow originals. Pay attention to rich text rendering, image proportions, and any sections that depend on specific field combinations.


The iteration loop is: fix the script, re-import the affected collection, re-run resolve-refs, check in Studio, fix the next thing. Most migrations need two or three passes before content is launch-ready.



### Pre-launch checklist



Use this before calling the migration done. Check each item in Studio or with a GROQ query.



### Schema and content model



- CMS types use `name`, page singletons use `title,` not mixed

- Slug source matches the correct field (`name` for CMS, `title` for pages) if added post-migration

- `figureImage` registered in schemaTypes/index.ts, not just inline in blockContent.ts


### Import pipeline



- `asset-map.json` populated (check CDN URLs and local export images)

- Pass 1 import complete → `id-map.json` exists with expected entry count

- `generate-url-map.js` run → `url-map.json` exists

- `import.js` re-run after url-map.json was generated (internal links use it)

- `resolve-refs.js` run after every re-import — check for any remaining `wf_*` placeholder refs

- All array fields (`tags`, `categories`, `sections`, `nav items`) include `_key` on every item


### SEO and pages



- Singleton pages have `title` + `slug` populated, not just `seo`

- CMS items have `seo.metaTitle` derived from detail page templates (not empty)

- Home page `title` is not the same as `seo.metaTitle`. These are different fields from different sources


### Navigation and globals



- CMS dropdown children seeded from NDJSON — not relying on HTML export placeholders

- `settings.global` singleton document exists

- `navigationMenu.*` documents exist with correct children


### Artifacts



- output/sitemap.xml generated (or next-sitemap configured)

- Redirects: redirects.json from API, or CSV export from Webflow dashboard

- forms-audit.json reviewed — every form has a replacement strategy


### Environment



- `SANITY_DATASET` in .env matches an existing dataset (`npx sanity datasets list`)

- `SANITY_TOKEN` has write permissions (Editor or above)


Next, you’ll set up forms, redirects, and sitemap.



---

## Related Resources

- [Full course as markdown](https://www.sanity.io/learn/course/migrating-content-from-webflow-to-sanity.md)
- [All courses and lessons](https://www.sanity.io/learn/sitemap.md)
- [Complete content for LLMs](https://www.sanity.io/learn/llms-full.txt)

<AgentInstructions>

## Submitting Feedback

If you encounter incorrect, outdated, or confusing documentation on this page, submit feedback:

POST https://www.sanity.io/learn/resource/feedback

```json
{
  "path": "/learn/course/migrating-content-from-webflow-to-sanity/validate-import-studio.md",
  "feedback": "Description of the issue"
}
```

Only submit feedback when you have something specific and actionable to report.

</AgentInstructions>
