# 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 * `

` → `style: "normal"` block * `
` → New line within block * Multiple `
` → New blocks ### Text Formatting | HTML | Portable Text Mark | Visual | | -------------- | --------------------------- | ----------------- | | `` | `marks: ["strong"]` | **Bold** | | `` | `marks: ["strong"]` | **Bold** | | `` | `marks: ["em"]` | *Italic* | | `` | `marks: ["em"]` | *Italic* | | `` | `marks: ["underline"]` | Underlined | | ``, `` | `marks: ["strike-through"]` | ~~Strikethrough~~ | | `` | `marks: ["code"]` | `Inline code` | ### Lists **Unordered lists:** ```html theme={null}

``` Becomes: ```json theme={null} [ {"_type": "block", "listItem": "bullet", "children": [...]}, {"_type": "block", "listItem": "bullet", "children": [...]} ] ``` **Ordered lists:** ```html theme={null}
  1. First step
  2. Second step
``` 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: Description

``` 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}

My Blog Post

This is bold and italic text.

Here's a link.

``` **Portable Text:** ```json theme={null} [ { "_type": "block", "style": "h2", "children": [{"text": "My Blog Post"}] }, { "_type": "block", "children": [ {"text": "This is "}, {"text": "bold", "marks": ["strong"]}, {"text": " and "}, {"text": "italic", "marks": ["em"]}, {"text": " text."} ] }, { "_type": "block", "children": [ {"text": "Here's a "}, {"text": "link", "marks": [{"_type": "link", "href": "/link"}]}, {"text": "."} ] } ] ```
**HTML:** ```html theme={null}
  • First item
  • Second item with bold
``` **Portable Text:** ```json theme={null} [ { "_type": "block", "listItem": "bullet", "children": [{"text": "First item"}] }, { "_type": "block", "listItem": "bullet", "children": [ {"text": "Second item with "}, {"text": "bold", "marks": ["strong"]} ] } ] ```
**HTML:** ```html theme={null}

A paragraph with bold link.

``` **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}

My Blog Post

This is important text with a link.

  • First item
  • Second item
```
```json theme={null} [ { "_type": "block", "style": "h2", "children": [{"_type": "span", "text": "My Blog Post"}] }, { "_type": "block", "style": "normal", "children": [ {"_type": "span", "text": "This is "}, {"_type": "span", "text": "important", "marks": ["strong"]}, {"_type": "span", "text": " text with a "}, {"_type": "span", "text": "link", "marks": [{"_type": "link", "href": "/link"}]}, {"_type": "span", "text": "."} ] }, { "_type": "block", "style": "normal", "listItem": "bullet", "children": [{"_type": "span", "text": "First item"}] }, { "_type": "block", "style": "normal", "listItem": "bullet", "children": [{"_type": "span", "text": "Second item"}] } ] ```
Learn more: [HTML Conversion](/concepts/html-conversion) ## Benefits for Migration ### Preservation of Structure HTML tags become semantic blocks: | HTML | Portable Text | Benefit | | ---------- | -------------------- | -------------------------------- | | `

` | `style: "h2"` | Heading semantics preserved | | `` | `marks: ["strong"]` | Emphasis meaning, not just bold | | `
  • ` | `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.