# Changelog
Source: https://docs.contentwrap.io/changelog
Track ContentWrap's latest features, improvements, and bug fixes
## Overview
Stay up to date with the latest ContentWrap releases, new features, and improvements. We're continuously working to make your Webflow to Sanity migrations faster, easier, and more reliable.
ContentWrap is currently in beta. We're actively developing new features and refining the migration experience based on user feedback.
## Latest Updates
### Coming Soon
We're working on several exciting features:
* **Reference Resolution**: Automatic handling of Webflow reference fields
* **Multi-Collection Import**: Migrate multiple collections in a single workflow
* **Schema Generation**: Auto-generate Sanity schema files from your mappings
* **Saved Mappings**: Reuse field mappings for recurring migrations
Want to be notified about new releases? Follow us on [X/Twitter](https://x.com/contentwrap) or check back here regularly.
## How to Report Issues
Found a bug or have a feature request?
* **Email**: [support@contentwrap.io](mailto:support@contentwrap.io)
* **Include**: Error messages, steps to reproduce, and your use case
***
*This changelog will be updated as we ship new features and improvements.*
# How Field Mapping Works
Source: https://docs.contentwrap.io/concepts/field-mapping
Understand MigrateKit's field mapping system and how it transforms Webflow CSV data into structured Sanity documents.
## What is Field Mapping?
Field mapping defines the relationship between your Webflow CSV columns and Sanity document fields. It tells MigrateKit:
1. **What** each column represents (title, body, image, etc.)
2. **How** to transform the data (HTML → Portable Text, URL → image)
3. **Where** to put it in your Sanity schema (field names)
\##The Mapping Process
MigrateKit reads your CSV headers and sample data to understand what you have
Based on column names and data types, MigrateKit suggests appropriate Sanity field types
Accept suggestions or manually configure mappings for each field
MigrateKit checks that all mappings are valid before allowing import
## Auto-Mapping Intelligence
MigrateKit uses pattern matching to suggest mappings:
### Name-Based Matching
| Webflow Column | Detected As | Suggested Type |
| --------------------------------- | ---------------- | -------------- |
| Name, Title, Heading | Title field | `string` |
| Slug, URL Slug | URL identifier | `slug` |
| Post Body, Content, Rich Text | Rich content | `portableText` |
| Main Image, Hero Image, Thumbnail | Primary image | `image` |
| Published Date, Date Published | Publication date | `datetime` |
| Featured, Active, Published | Toggle field | `boolean` |
### Data-Based Detection
MigrateKit also inspects sample values:
* **ISO dates** (`2024-01-15T10:30:00.000Z`) → `datetime`
* **URLs** (`https://...`) → `url` or `image`
* **Numbers** (`100`, `3.14`) → `number`
* **True/false** (`true`, `false`, `yes`, `no`) → `boolean`
* **HTML tags** (`
`, `
`) → `portableText`
## Transformation Pipeline
When you map a field, MigrateKit applies transformations during import:
```mermaid theme={null}
graph LR
A[CSV Value] --> B{Field Type}
B -->|string| C[Clean Text]
B -->|portableText| D[HTML → PT]
B -->|image| E[Download & Upload]
B -->|slug| F[Sanitize]
B -->|datetime| G[Parse Date]
C --> H[Sanity Document]
D --> H
E --> H
F --> H
G --> H
```
### String Fields
* Trim whitespace
* Remove null bytes
* Preserve Unicode
### Portable Text
1. Parse HTML structure
2. Map tags to blocks
3. Extract inline formatting
4. Create Portable Text JSON
Learn more: [HTML Conversion](/concepts/html-conversion)
### Image Fields
1. Validate URL format
2. Download from source
3. Check for duplicates
4. Upload to Sanity
5. Create asset reference
### Slug Fields
1. Lowercase value
2. Remove special characters
3. Replace spaces with hyphens
4. Create slug object: `{current: "value"}`
### Date/Time Fields
1. Parse various formats:
* ISO 8601: `2024-01-15T10:30:00.000Z`
* Simple date: `2024-01-15`
* US format: `01/15/2024` (if unambiguous)
2. Convert to ISO format
3. Validate range (1900-2100)
### Number Fields
1. Parse as float or integer
2. Handle separators (`,` `1,000` → `1000`)
3. Validate numeric
4. Preserve decimal precision
### Boolean Fields
Convert various representations:
| CSV Value | Boolean |
| ------------------------- | ------- |
| `true`, `True`, `TRUE` | `true` |
| `false`, `False`, `FALSE` | `false` |
| `yes`, `Yes`, `YES`, `1` | `true` |
| `no`, `No`, `NO`, `0` | `false` |
## Validation Rules
MigrateKit validates mappings before import:
### Schema Validation
* **Document type** - Valid Sanity identifier (no spaces, special chars)
* **Field names** - Unique, valid identifiers
* **Required fields** - Must have mappings
### Data Validation
* **Type compatibility** - CSV values can convert to target type
* **Required data** - Required fields have values in CSV
* **Format validity** - Dates parse, URLs are valid, etc.
### Preview Validation
Sample documents show you exactly what will be imported:
## Common Mapping Patterns
### Basic Blog Post
| Webflow Column | Sanity Field | Type |
| -------------- | ------------ | -------------- |
| Name | title | `string` |
| Slug | slug | `slug` |
| Post Body | content | `portableText` |
| Main Image | coverImage | `image` |
| Published Date | publishedAt | `datetime` |
| Featured | featured | `boolean` |
### E-commerce Product
| Webflow Column | Sanity Field | Type |
| -------------- | ------------ | ------------------ |
| Name | title | `string` |
| Slug | slug | `slug` |
| Description | description | `portableText` |
| Price | price | `number` |
| Product Image | images | `array` of `image` |
| In Stock | inStock | `boolean` |
### Author/Person
| Webflow Column | Sanity Field | Type |
| -------------- | ------------ | -------------- |
| Name | name | `string` |
| Slug | slug | `slug` |
| Bio | bio | `portableText` |
| Photo | photo | `image` |
| Email | email | `string` |
| Twitter Handle | twitter | `string` |
## Advanced Mapping
### Arrays
Map comma-separated values to arrays:
**CSV:**
```csv theme={null}
Tags
"webflow,sanity,migration"
```
**Mapping:**
* Field type: `array`
* Item type: `string`
**Result:**
```json theme={null}
{
"tags": ["webflow", "sanity", "migration"]
}
```
### Nested Objects
While MigrateKit doesn't support complex nested mappings in MVP, you can:
1. Import flat data
2. Manually restructure in Sanity Studio
3. Or use Sanity's GROQ projections after import
## Best Practices
Choose names that clearly indicate content:
* ✅ `coverImage`, `heroImage`
* ❌ `img`, `pic1`, `image-main`
Good names make your Sanity schema self-documenting.
Use camelCase for field names:
* ✅ `publishedAt`, `authorName`
* ❌ `published_at`, `author-name`
This matches Sanity's standard patterns.
You don't have to map every column:
* Skip Webflow system fields (`Collection ID`, `Item ID`)
* Skip deprecated or unused fields
* Skip fields you'll add manually later
Less clutter = easier schema maintenance.
If columns reference other collections:
1. Note the relationship
2. Map as `string` for now (stores the ID/slug)
3. Manually recreate references in Sanity after import
Future: MigrateKit will support automatic reference resolution.
Always review sample documents before importing:
* Check Portable Text renders correctly
* Verify dates parse properly
* Confirm images reference correctly
## Troubleshooting
**Cause:** Data in column doesn't match selected field type.
**Example:** Mapping text column to `number` field.
**Fix:**
* Choose correct field type for the data
* OR clean data in CSV before upload
**Cause:** Some rows are missing data for a required field.
**Fix:**
* Make field optional (uncheck "Required")
* OR fill missing values in CSV
* OR accept that rows with missing data will fail
**Cause:** Column name or data pattern was ambiguous.
**Fix:** Manually adjust the mapping—auto-suggestions are just starting points.
**Current limitation:** Reference fields aren't supported in MVP.
**Workaround:**
* Map as `string` to preserve the ID/slug
* Manually link references in Sanity Studio after import
# HTML to Portable Text Conversion
Source: https://docs.contentwrap.io/concepts/html-conversion
Understand how MigrateKit intelligently converts Webflow's HTML rich text to Sanity's Portable Text format with semantic preservation.
## How Conversion Works
MigrateKit automatically converts HTML from Webflow's rich text fields into Sanity's Portable Text format during migration. This process:
1. **Parses HTML** - Reads your Webflow HTML structure
2. **Maps tags to blocks** - Converts HTML tags to Portable Text blocks and marks
3. **Preserves semantics** - Maintains meaning, not just visual appearance
4. **Handles edge cases** - Wraps unsupported content as code blocks
## Supported HTML Tags
MigrateKit handles most common HTML tags from Webflow:
### Headings
| HTML | Portable Text | Usage |
| ------ | ------------- | ------------------- |
| `
` | `style: "h1"` | Top-level headings |
| `
` | `style: "h2"` | Section headings |
| `
` | `style: "h3"` | Subsection headings |
| `
` | `style: "h4"` | Minor headings |
| `
` | `style: "h5"` | Rarely used |
| `
` | `style: "h6"` | Rarely used |
### Paragraphs & Line Breaks
* `
```
Becomes:
```json theme={null}
[
{"_type": "block", "listItem": "number", "children": [...]},
{"_type": "block", "listItem": "number", "children": [...]}
]
```
**Nested lists** are supported with proper indentation levels.
### Links
```html theme={null}
Click here
```
Becomes:
```json theme={null}
{
"_type": "span",
"text": "Click here",
"marks": [{
"_type": "link",
"href": "https://example.com"
}]
}
```
**Link attributes:**
* `href` - Preserved as `href`
* `target="_blank"` - Preserved as `blank: true`
* `rel` - Preserved if present
### Images (Inline)
Images within rich text are converted to image blocks:
```html theme={null}
Here's an image:
```
Becomes separate blocks:
```json theme={null}
[
{"_type": "block", "children": [{"text": "Here's an image:"}]},
{"_type": "image", "asset": {...}, "alt": "Description"}
]
```
## Handled with Warnings
Some HTML requires special handling:
### Inline Styles
```html theme={null}
Red text
```
Inline `style` attributes are **stripped** during conversion. Portable Text doesn't preserve inline CSS.
**Why?** Portable Text separates content from presentation. You style content in your frontend, not in the CMS.
### Custom Classes
```html theme={null}
Content
```
Class names are **not preserved**. Portable Text has no concept of CSS classes.
**Solution:** Use custom Portable Text blocks in Sanity for special formatting needs.
## Unsupported Content → Code Blocks
Anything we can't map to a Portable Text block/mark is wrapped as a `code` block with `language: "html"`. Examples:
### Scripts & Embeds
```html theme={null}
```
Becomes:
```json theme={null}
{
"_type": "code",
"language": "html",
"code": ""
}
```
### Iframes
```html theme={null}
```
Becomes:
```json theme={null}
{
"_type": "code",
"language": "html",
"code": ""
}
```
### Custom Webflow Elements & Unknown Tags
```html theme={null}
Content
```
Becomes:
```json theme={null}
{
"_type": "code",
"language": "html",
"code": "Content"
}
```
These appear as code in Sanity and won't execute. Replace with Sanity-native components (e.g., YouTube plugin or custom embed blocks) after import.
## Conversion Examples
**HTML:**
```html theme={null}
```
**Portable Text:**
```json theme={null}
[
{
"_type": "block",
"children": [
{"text": "A paragraph with "},
{
"text": "bold link",
"marks": ["strong", {"_type": "link", "href": "/link"}]
},
{"text": "."}
]
}
]
```
## Best Practices
### Before Migration
Remove unnecessary HTML before exporting:
* Delete empty `
` and `` tags
* Remove inline styles you don't need
* Simplify complex nested structures
Note any Webflow-specific elements that will need replacement:
* Custom interactions
* Dynamic embeds
* Complex grids or layouts
Export a small collection first to see how your HTML converts
### After Migration
Check all code blocks in Sanity Studio:
* Replace scripts with Sanity-safe alternatives
* Convert iframes to proper embeds
* Remove unnecessary preserved HTML
Adjust any conversion quirks:
* Incorrect heading levels
* Lost spacing
* Misinterpreted formatting
Enhance content with Portable Text features:
* Custom block types
* Better link handling
* Responsive embeds
## Common Issues
**Cause:** Multiple ` ` tags or `
` tags with empty content.
**Fix:** Manually add paragraph breaks in Sanity Studio where needed.
**Cause:** Inline styles or classes were stripped during conversion.
**Fix:** Portable Text separates content from styling. Style your content in your frontend rendering, not in the CMS.
**Cause:** Images didn't upload (network error, quota exceeded).
**Fix:** Check the import report for asset failures and manually upload missing images.
**Cause:** Relative Webflow links may not work in new context.
**Fix:** Update links in Sanity Studio to point to correct URLs.
**Expected:** MigrateKit preserves unknown HTML as code blocks for safety.
**Fix:** Review code blocks and replace with Sanity-compatible alternatives.
# Understanding Portable Text
Source: https://docs.contentwrap.io/concepts/portable-text
Learn about Sanity's Portable Text format and how MigrateKit converts HTML to structured, platform-agnostic content.
## What is Portable Text?
Portable Text is Sanity's rich text format—a structured, JSON-based way to represent content that's more powerful and flexible than HTML.
Unlike HTML (which mixes content and presentation), Portable Text separates **what** the content is from **how** it looks. This makes your content:
* **Platform-agnostic** - Render on web, mobile, email, or anywhere
* **Structured** - Query and filter content programmatically
* **Customizable** - Style differently across platforms
* **Future-proof** - Not tied to specific HTML/CSS
## Portable Text Structure
Portable Text is an array of blocks and marks:
```json theme={null}
[
{
"_type": "block",
"style": "h2",
"children": [
{
"_type": "span",
"text": "Welcome to Sanity",
"marks": []
}
]
},
{
"_type": "block",
"style": "normal",
"children": [
{
"_type": "span",
"text": "This is a ",
"marks": []
},
{
"_type": "span",
"text": "paragraph",
"marks": ["strong"]
},
{
"_type": "span",
"text": " with formatting.",
"marks": []
}
]
}
]
```
This represents:
```html theme={null}
Welcome to Sanity
This is a paragraph with formatting.
```
## Key Concepts
### Blocks
**Blocks** are the main content units:
* **Paragraphs** - `style: "normal"`
* **Headings** - `style: "h1"` through `style: "h6"`
* **Lists** - `listItem: "bullet"` or `listItem: "number"`
* **Custom blocks** - Images, code, embeds, etc.
### Marks
**Marks** are inline annotations:
* `strong` - Bold text
* `em` - Italic text
* `underline` - Underlined text
* `code` - Inline code
* `link` - Hyperlinks with `href`
### Spans
**Spans** are text segments within blocks, each with its own marks.
## How MigrateKit Converts HTML
When you migrate from Webflow, MigrateKit transforms HTML to Portable Text:
```html theme={null}
` | `listItem: "bullet"` | List structure, not just bullets |
### Platform Flexibility
Render Portable Text differently on different platforms:
```tsx theme={null}
// Web - styled as H1
{heading}
// Mobile - styled as H2 for smaller screens
{heading}
// Email - inline styles
{heading}
```
All from the same Portable Text source!
### Querying and Filtering
Find content programmatically:
```groq theme={null}
// Find all documents with links to /pricing
*[_type == "post" && pt::text(body) match "*pricing*"]
```
You can't do this easily with HTML.
## Editing Portable Text
In Sanity Studio, Portable Text appears as a rich text editor:
Editors see familiar formatting options:
* Bold, italic, underline buttons
* Heading dropdown
* List buttons
* Link insertion
* Undo/redo
But behind the scenes, it's all stored as structured Portable Text.
## Custom Block Types
Portable Text isn't limited to text—you can embed custom content:
```json theme={null}
[
{
"_type": "block",
"children": [{"_type": "span", "text": "Check out this image:"}]
},
{
"_type": "image",
"asset": {"_ref": "image-abc123-jpg"}
},
{
"_type": "callout",
"tone": "warning",
"text": "Important notice!"
}
]
```
This allows rich, interactive content while keeping structure.
## Why This Matters for Webflow Migrations
### From HTML Chaos to Structured Content
Webflow stores rich text as HTML—a mix of content, structure, and styling. Portable Text separates these concerns:
| Aspect | HTML (Webflow) | Portable Text (Sanity) |
| --------------- | ------------------------- | ------------------------------- |
| **Content** | Mixed with tags | Pure text in `children` |
| **Structure** | Implicit in tags | Explicit in `_type` and `style` |
| **Formatting** | CSS classes/inline styles | Semantic marks |
| **Portability** | Web-specific | Platform-agnostic |
### Better Content Management
* **Cleaner** - No HTML tag soup
* **Safer** - No script injection risks
* **Flexible** - Render anywhere, any way
* **Searchable** - Query content semantically
## Common Questions
Yes! Sanity supports HTML in Portable Text through custom serializers. But you lose some benefits:
* Can't query HTML content semantically
* Platform-specific rendering is harder
* Less structured
MigrateKit converts HTML to Portable Text to give you maximum flexibility.
MigrateKit handles most common HTML tags, but complex structures may need manual cleanup:
* Custom Webflow elements → Code blocks (review in Studio)
* Nested formatting → Simplified to Portable Text rules
* Unsupported tags → Wrapped for safety
See [HTML Conversion](/concepts/html-conversion) for details.
In Sanity Studio, you edit using the rich text editor (WYSIWYG). To see the raw Portable Text JSON, use Sanity's Vision plugin.
You typically don't need to edit Portable Text as JSON—the editor handles it.
Portable Text is an **open specification** created by Sanity. While Sanity uses it extensively, you can use Portable Text in any system. It's just JSON!
# Welcome to MigrateKit
Source: https://docs.contentwrap.io/introduction
Migrate Webflow content to Sanity CMS with automated CSV import. Built by ContentWrap.
## What is MigrateKit?
MigrateKit is a CSV migration tool built by [ContentWrap](https://contentwrap.io), the team behind enterprise Sanity implementations like Ramp's 1,500+ document migration.
Upload your Webflow CSV export, map fields to your Sanity schema, and import everything as drafts—ready for review before publishing.
## Why MigrateKit?
Migrating CMS platforms manually is slow and error-prone. MigrateKit handles the tedious work:
* **Smart field mapping** - Visual interface to connect Webflow columns to Sanity fields
* **HTML to Portable Text** - Automatically converts rich text while preserving formatting
* **Asset migration** - Downloads images from Webflow and uploads them to Sanity
* **Safe drafts** - All imports create drafts, protecting your published content
* **Webflow-optimized** - Built specifically for Webflow's CSV export format
## How It Works
Download your collection as a CSV from Webflow CMS
Upload the CSV and map Webflow fields to your Sanity schema
Link your Sanity project using your project ID and API token
Review and execute the migration—all content imports as drafts
## Who Should Use MigrateKit?
Migrate without technical expertise or custom scripts
Save days of manual work and focus on building features
Offer fast, reliable migration services to clients
Streamline client onboarding and reduce project timelines
## Get Started
You'll need a **ContentWrap account** to use MigrateKit. Sign up for free, then navigate to **MigrateKit** in your sidebar or visit `/migratekit` to begin.
What you need before starting
Complete your first migration in under 10 minutes
Full step-by-step migration roadmap
# Migration Overview
Source: https://docs.contentwrap.io/migration/overview
Understand MigrateKit's 4-step CSV migration process from Webflow to Sanity with automated field mapping and HTML conversion.
Use this page for the high-level flow. The canonical product behavior lives in the four step pages below.
## The 4-Step Process
MigrateKit's migration workflow is designed to be straightforward while giving you complete control over how your content transforms.
Add your Project ID, dataset, and API token in ContentWrap
Download your collection as a CSV file
Upload CSV and map Webflow columns to Sanity fields
Execute migration and review drafts in Sanity Studio
## What Happens During Migration
You define how Webflow data becomes Sanity documents:
* Name your Sanity document type (e.g., `blogPost`)
* Map each Webflow column to a Sanity field type
* MigrateKit auto-suggests mappings based on data patterns
* Skip system fields like `Collection ID` and `Item ID`
Rich text fields are automatically converted:
* HTML tags become Portable Text blocks
* Headings, paragraphs, lists preserved
* Bold, italic, links maintained
* Images embedded in content
* Unsupported tags wrapped as code blocks
Learn more: [HTML Conversion](/concepts/html-conversion)
Images and files are handled automatically:
* Downloads assets from Webflow CDN
* Checks for duplicates (reuses existing)
* Uploads to your Sanity project
* Creates proper asset references
* Reports upload status for each file
Learn more: [Asset Handling](/concepts/field-mapping#image-fields)
All content imports as drafts for safety:
* Documents created with `drafts.` prefix
* Published content remains untouched
* Review and edit before publishing
* Publish individually or in bulk
## Migration Flow
```mermaid theme={null}
graph LR
A[Connect Sanity] --> B[Export Webflow CSV]
B --> C[Upload CSV]
C --> D[Map & Preview]
D --> E[Import to Sanity]
E --> F[Review Drafts]
F --> G[Publish]
```
## Step-by-Step Guides
Follow detailed instructions for each phase:
Link your Sanity project with API credentials
How to export CSVs from Webflow correctly
Upload CSV and define field mappings
Execute migration and verify results
## Best Practices
* Test with a small collection (10-50 items)
* Use a development dataset first
* Verify the workflow works
* Refine mappings before scaling
* Save original CSV files
* Don't delete Webflow data until verified
* Export from Sanity for additional backup
* Decide on document type names
* Choose appropriate field types
* Consider Sanity naming conventions
* Think about future content structure
* Check sample documents in Sanity
* Verify Portable Text formatting
* Confirm images loaded correctly
* Test any links or embedded content
## Current Limitations
**Single Collection Per Import**
MigrateKit currently handles one collection at a time. For sites with multiple related collections:
* Migrate independent collections first
* Note relationships for manual recreation
* Rebuild references in Sanity after import
Multi-collection support with automatic reference resolution is on the roadmap.
**No Reference Field Support**
Cross-collection references aren't automatically migrated. You'll need to manually recreate relationships in Sanity Studio after import.
See [Limitations](/reference/limitations) for a complete list.
## Get Started
Ready to migrate? Choose your path:
Fast track migration guide
What you need before starting
## Need Help?
Common issues and solutions
# Step 1: Connect Your Sanity Project
Source: https://docs.contentwrap.io/migration/step-1-connect-sanity
Start by linking ContentWrap to your Sanity project using your project ID, dataset name, and API token.
## Before You Begin
You'll need three pieces of information from your Sanity project:
1. **Project ID** - Found in your Sanity dashboard
2. **Dataset name** - Usually `production` or `development`
3. **API Token** - Must have **Editor** permissions
See prerequisites for detailed instructions
## Find Your Sanity Credentials
### Get Your Project ID
Navigate to [sanity.io/manage](https://sanity.io/manage)
Click on the project you want to migrate content into
Find the Project ID in the project settings or URL
Example: `abc123xyz`
### Choose Your Dataset
Your dataset is where content will be imported. Common options:
* **`production`** - Your live, published content
* **`development`** - Test/staging environment
* **`staging`** - Pre-production environment
**First migration?** Use `development` or `staging` to test the migration safely before importing to `production`.
### Create an API Token
In your Sanity project management console, click **API** in the sidebar
Create a new token with these settings:
* **Name**: MigrateKit Migration
* **Permissions**: **Editor** (required for creating documents)
Save it somewhere safe—you won't be able to see it again
## Connect in MigrateKit
### Add Your Sanity Project
If you haven't added a Sanity project yet:
In your ContentWrap dashboard (app.contentwrap.io), navigate to **Settings** → **Sanity Projects**
Fill in the form:
* **Project ID**: Your Sanity project ID
* **Dataset**: Your dataset name
* **API Token**: The token you created
Click **Test Connection** to verify credentials
You should see "Connection successful" with your project name
Click **Save** to store your Sanity project in your account
### Select an Existing Project
If you've already connected a Sanity project:
Select your project from the **Sanity Project** dropdown
Choose which dataset to import into
## Manage Document Types
After connecting a project, define which document types to use for migrations. You can add them manually or sync from your deployed Sanity dataset.
* **Add type manually**: Create a document type entry yourself
* **Sync types from Sanity**: Pull existing document types from your deployed dataset (recommended if schemas are already deployed)
* Shows counts per type (documents detected) and schema status
## Manage Schema Fields
For each document type, set up schema fields before mapping CSV columns. You can add fields manually or sync from Sanity.
* **Sync fields from Sanity**: Pull field definitions from deployed schema
* **Add field manually**: Define Sanity field name and type yourself
* MigrateKit attempts to infer types from existing values; you can override any field type
## Troubleshooting Connection Issues
**Causes:**
* Token was copied incorrectly (extra spaces/characters)
* Token doesn't have Editor permissions
* Token was revoked or deleted
**Fix:**
* Create a new token with Editor permissions
* Copy the entire token without extra spaces
* Make sure you're using the correct project
**Causes:**
* Project ID is incorrect
* Typing error in Project ID
* Using project name instead of ID
**Fix:**
* Double-check Project ID in your Sanity dashboard
* Copy-paste instead of typing manually
* Project ID is alphanumeric (e.g., `abc123xyz`), not the project name
**Causes:**
* Dataset name is misspelled
* Dataset doesn't exist in this project
* Capitalization is wrong
**Fix:**
* Verify dataset name in Sanity project settings
* Dataset names are case-sensitive
* Create the dataset in Sanity if it doesn't exist
**Cause:** API token doesn't have Editor permissions
**Fix:**
* Create a new token with **Editor** role
* Delete the old token for security
* Update the token in your dashboard
## Security & Best Practices
### Protect Your API Token
* **Never commit tokens to git** - Add to `.gitignore`
* **Don't share publicly** - Treat like a password
* **Revoke unused tokens** - Delete tokens you're not using
* **Use environment variables** - For automation/scripts
### Token Management
* **Create project-specific tokens** - One per project/tool
* **Name tokens clearly** - E.g., "MigrateKit Migration - Production"
* **Rotate periodically** - Create new tokens, revoke old ones
* **Revoke if compromised** - Immediately delete and create new
### Multiple Projects
You can connect multiple Sanity projects to your ContentWrap account:
* Each project saved separately
* Switch between projects during migration
* Test in development, import to production
# Step 2: Export from Webflow
Source: https://docs.contentwrap.io/migration/step-2-export
Learn how to properly export your Webflow CMS collection as a CSV file for migration to Sanity using ContentWrap.
## Export Your Collection
In Webflow Designer or Editor, click **CMS** → **Collections**
Click the collection you want to migrate
Click the settings icon (⚙️) → **Export** → **CSV file**
Webflow downloads a file named `Collection Name.csv`
Store the file safely and **make a backup copy**
You should now have a CSV file with all your collection's content, including all fields and metadata.
## Critical: Don't Edit These Columns
MigrateKit relies on Webflow's system fields to prevent duplicates and track content. **Never modify or delete:**
**Required Webflow Columns**
* `Collection ID` - Identifies the source collection
* `Item ID` - Unique identifier for each item (used to prevent duplicates)
* `Locale ID` - Language/locale information
If these columns are missing or modified, ContentWrap cannot properly track and update documents.
### Don't Rename Headers
Keep all column headers exactly as Webflow exports them:
| ❌ Wrong | ✅ Correct |
| -------------- | ------------ |
| `Post Title` | `Name` |
| `Article Body` | `Post Body` |
| `Hero` | `Main Image` |
Renaming headers breaks MigrateKit's auto-mapping feature.
## Safe Modifications
You CAN make these changes without breaking the migration:
Remove rows for content you don't want to migrate:
* Draft items
* Test content
* Archived posts
**Better approach:** Import everything and delete unwanted drafts in Sanity instead. This gives you more flexibility.
Delete columns that are completely empty (no data in any row):
* Blank columns added by spreadsheet software
* Trailing empty columns
Only delete if **every single row** is empty in that column!
Delete empty rows at the end of the file after all your data
## Understanding Webflow CSV Structure
Knowing how Webflow formats different field types helps you plan your Sanity mapping:
### Text Fields
Plain text exports as simple strings:
```csv theme={null}
Name,Slug
My Blog Post,my-blog-post
```
### Rich Text (HTML)
Rich content exports as HTML in quoted cells:
```csv theme={null}
Post Body
"
` tags were stripped
**Solution:** Manually add paragraph breaks in Sanity Studio where needed
**Symptoms:** Images that were in Webflow content don't appear
**Causes:**
* Image URLs in HTML were invalid
* Images failed to download
* Asset upload quota exceeded
**Solutions:**
* Check import report for asset failures
* Verify original URLs in CSV are complete
* Manually upload missing images in Sanity
**Expected behavior:** Scripts and iframes are preserved as code blocks for security
**What to do:**
* Review code blocks in Sanity Studio
* Replace with Sanity-compatible alternatives:
* YouTube embeds → Sanity YouTube plugin
* Custom scripts → Sanity-safe implementations
* See [HTML Conversion](/concepts/html-conversion#unsupported-content)
## Import Errors
**Symptoms:** Import completes but some rows show errors
**Common causes:**
* Required fields with missing values
* Data type validation failures
* Invalid Portable Text from malformed HTML
**Solutions:**
* Review error messages for specific rows
* Fix issues in CSV
* Re-upload and import just the failed rows
**Symptoms:** Import takes much longer than estimated
**Causes:**
* Large number of high-resolution images
* Many documents (>500)
* Complex Portable Text requiring heavy processing
**Solutions:**
* Be patient—large imports take time
* Split into smaller batches
* Optimize/resize images before import
**Symptoms:** Documents import but images are missing
**Causes:**
* Invalid image URLs in CSV
* Network timeouts
* Sanity asset quota exceeded
* Images too large
**Solutions:**
* Check import report for specific failures
* Verify URLs are accessible
* Check Sanity asset quota limits
* Manually upload failed assets
**Symptoms:** Import fails with duplicate document ID error
**Cause:** Trying to import same data twice
**Solution:**
* Delete existing drafts in Sanity first
* OR MigrateKit will update existing documents (using `Item ID`)
## Data Quality Issues
**Symptoms:** Dates appear incorrect in Sanity
**Causes:**
* Excel auto-formatted dates in CSV
* Timezone conversion issues
* Invalid date format
**Solutions:**
* Don't open CSV in Excel (use Google Sheets)
* Verify dates in text editor before upload
* Check original Webflow data for accuracy
**Symptoms:** Links don't work or point to wrong URLs
**Causes:**
* Relative Webflow URLs don't work in new context
* Links to Webflow-specific pages
**Solutions:**
* Update links in Sanity Studio to absolute URLs
* Review all links after migration
* Convert Webflow internal links to new site structure
**Symptoms:** Boolean field shows text instead of checkbox
**Cause:** Field mapped as `string` instead of `boolean`
**Solutions:**
* Re-map field as `boolean` type
* MigrateKit converts "true"/"false"/"yes"/"no" automatically
**Symptoms:** `100.50` became `100.5` or similar
**Cause:** Number precision issue
**Solution:** This is expected—trailing zeros are mathematically irrelevant. If you need to preserve exact formatting (like prices), use `string` type and format in your frontend.
## Need More Help?
If your issue isn't listed here:
Ensure you have everything set up correctly
Step-by-step instructions for the entire process
Current features and known limitations
Email [support@contentwrap.io](mailto:support@contentwrap.io)