---
title: "Duda to Sitecore Migration: A Technical Guide"
slug: duda-to-sitecore-migration-a-technical-guide
date: 2026-08-20
author: Raaj
categories: [Migration Guide]
excerpt: "A technical guide to migrating from Duda to Sitecore: extraction methods, content model mapping, GraphQL and SPE import pipelines, and what breaks."
tldr: "Duda exports flat HTML and RSS — Sitecore requires structured items. Every Duda-to-Sitecore migration is a custom ETL job: extract via API, ZIP, and scraping, then load via the Authoring GraphQL API or SPE."
canonical: https://clonepartner.com/blog/duda-to-sitecore-migration-a-technical-guide/
---

# Duda to Sitecore Migration: A Technical Guide


Migrating from [Duda](https://www.duda.co/) to [Sitecore](https://www.sitecore.com/) means moving from a visual, agency-focused website builder with limited export capabilities to an enterprise digital experience platform (DXP) with deep content modeling, personalization engines, and programmatic control over every content item.

The core challenge: **Duda's export gives you flat HTML, not structured content.** Sitecore requires structured items organized into templates, components, and a content tree hierarchy. No native connector, migration wizard, or import button bridges these two platforms. Every migration from Duda to Sitecore is a custom extraction, transformation, and loading (ETL) job. ([support.duda.co](https://support.duda.co/hc/en-us/articles/26519246857495-Site-Export?utm_source=openai))

Teams typically make this move when they've outgrown Duda's page limits, need enterprise personalization, require multi-site management at scale, or want programmatic control over content workflows. The trade-off is real: you gain Sitecore's composable architecture, headless delivery via Experience Edge, and a full authoring API — but you take on significant complexity in infrastructure, licensing, and content modeling that Duda deliberately abstracts away.

This guide covers extraction methods from Duda, the architectural mismatch between the two platforms, Sitecore's import pathways, content type mapping, XP vs. XM Cloud decision criteria, and the things that will break or require manual rebuilding.

> [!WARNING]
> **No native migration path exists.** Duda's site export produces static HTML/CSS/JS bundles. Sitecore ingests structured content via its Authoring and Management GraphQL API or Sitecore PowerShell Extensions. Bridging these two requires custom scripts to parse Duda's output and create Sitecore items programmatically.

## Why Teams Move From Duda to Sitecore

Duda is built for agencies managing many small-to-mid-size client websites. It excels at visual design speed, white-label management, and publishing similar sites at scale. But it has hard architectural limits that push teams toward enterprise CMS platforms:

- **Page caps:** Duda enforces a maximum of 1,000 site pages and 1,500 blog pages per site. Dynamic pages bypass this count, but the content model is still collection-bound.
- **API constraints:** Duda's Partner API is a management API, not a content API. It handles site lifecycle — creation, publishing, access control — not granular page content extraction. Full API access requires the Custom plan. ([developer.duda.co](https://developer.duda.co/reference/getting-started-with-the-duda-api?utm_source=openai))
- **No headless delivery:** Duda renders pages server-side with its own engine. There's no headless mode, no GraphQL endpoint for content delivery, and no way to serve Duda content to native apps or custom frontends.
- **Limited personalization:** Duda offers basic personalization triggers (location, UTM, device), but nothing approaching Sitecore's rule-based, segment-driven personalization engine.

Sitecore, by contrast, is an enterprise DXP. With XM Cloud (the current SaaS offering), you get headless content management, Experience Edge for CDN-backed GraphQL delivery, composable architecture, multi-site/multi-language management, and deep personalization. The cost and complexity are proportionally higher.

## XP vs. XM Cloud: Which Migration Path Applies to You

The migration toolchain differs significantly depending on your Sitecore target. Resolve this before writing a single script.

| Decision Dimension | Sitecore XP / XM (On-Prem / PaaS) | Sitecore XM Cloud (SaaS) |
|---|---|---|
| Primary import tool | Sitecore PowerShell Extensions (SPE) | Authoring & Management GraphQL API |
| Secondary tool | Sitecore Content Serialization (SCS) | Sitecore Content Serialization (SCS) |
| SPE available? | Yes | **No** |
| Database model | master / web / publishing targets | master → Experience Edge (CDN) |
| Publishing target | web database → CD servers | Experience Edge GraphQL layer |
| Rendering approach | MVC / SXA | Next.js rendering host (JSS) |
| Component framework | Sitecore Layout Service or server-side MVC | JSS + Experience Edge GraphQL |
| Recommended for | Existing XP investments, on-prem compliance requirements | Greenfield Sitecore builds, SaaS-first orgs |

**Use SPE** if you're loading content into XP 9.x or XM 10.x on-premises or on a managed cloud host. SPE runs inside the Sitecore application context, bypasses HTTP serialization overhead, and gives you direct Item API access. For a 500-item import, SPE batch operations typically complete in under 5 minutes on standard hardware.

**Use the Authoring & Management GraphQL API** if your target is XM Cloud. SPE is not available in XM Cloud. The GraphQL API is REST-over-HTTP, meaning network latency and per-mutation overhead matter at scale. At typical throughput (sequential mutations, no batching), expect roughly 200–400 items per hour depending on field count and payload size. Use parallel workers with a concurrency limit of 3–5 to avoid hitting undocumented throttle thresholds; the documented rate limit applies at the authentication token scope.

**XP vs. XM Cloud database model distinction:** In XP/XM, content flows from `master` (authoring) to `web` (delivery) via a publish operation. CD servers read from `web`. In XM Cloud, there is no `web` database — content publishes from `master` directly to Experience Edge, Sitecore's CDN-backed GraphQL delivery layer. When you call `publishItem` in XM Cloud, the target database is `experienceedge`, not `web`. XP users targeting the wrong database in scripts is one of the most common porting errors when repurposing XM Cloud examples for XP environments.

## The Architectural Mismatch

Before writing a single extraction script, understand how fundamentally different the content models are.

**Duda's model** is page-centric and visual. Content lives inside a drag-and-drop editor as widgets (text, image, button, form, gallery) placed onto page sections. There is no content tree, no template inheritance, no field-level content modeling. Dynamic pages pull from "collections" — flat tabular data sources — but the relationship is one template design → many generated pages, not a hierarchical content architecture. The content does not exist independently of the page layout.

**Sitecore's model** is item-centric and hierarchical. Every piece of content is a Sitecore Item stored in a content tree under `/sitecore/content/`. Each item is based on a **Data Template** that defines its fields (single-line text, rich text, image, link, etc.). Presentation is handled separately via Layouts and Renderings. When a user requests a page, Sitecore dynamically assembles the item's content using the components defined in its Presentation Details.

| Concept | Duda | Sitecore XM Cloud |
|---|---|---|
| Content storage | Widget-based, embedded in page | Item-based, content tree hierarchy |
| Page structure | Sections → Rows → Widgets | Page Items → Placeholder → Component renderings |
| Templates | Visual page templates | Data Templates with typed fields |
| Blog | Built-in blog module (RSS export) | Custom blog template + item buckets |
| Dynamic content | Collections → Dynamic Pages | Datasource items → Component renderings |
| Media | Media Manager (per-site) | Media Library (shared, versioned) |
| API (read) | Partner REST API (management-focused) | Experience Edge GraphQL |
| API (write) | Partner API (site lifecycle only) | Authoring & Management GraphQL API |
| Multi-language | Basic language support | Full, per-field language versioning |
| Personalization | Location/UTM/device triggers | Rule-based, segment-driven, analytics-fed |

The primary engineering challenge of this migration is transformation: parse Duda's visual widget structure, extract the raw text and media, and map those distinct elements into Sitecore's structured fields. If you copy Duda's rendered HTML directly into Sitecore Rich Text fields, you bypass Sitecore's entire architectural value — personalization, omnichannel delivery, and headless scalability become impossible.

## How to Extract Content From Duda

There are three extraction pathways, and most migrations will combine all three.

### 1. Duda Site Export (Static HTML Bundle)

Duda's built-in site export produces a `.zip` file containing the published site's HTML, JavaScript, CSS, images, and media files. This is available on the Agency plan and higher. Duda serves device-specific content, so the export generates separate HTML/CSS folders for desktop, tablet, and mobile. ([support.duda.co](https://support.duda.co/hc/en-us/articles/26519246857495-Site-Export?utm_source=openai))

**What you get:**
- Static HTML files for each published page
- CSS and JS assets
- Images and media files
- An `.rss` file containing blog post content

**What the RSS file contains (and omits):**
The RSS export includes: post title, published date, post slug/URL, full body content (as `<content:encoded>` CDATA), and excerpt. It typically omits: author name, per-post SEO meta description, OG image URL, post-level canonical tag, and tag/category taxonomy (categories may appear as `<category>` elements but are not guaranteed to match Duda's internal taxonomy IDs). Verify your specific export against this list before building your transformation script.

**What you don't get from the ZIP:**
- Dynamic page content (collection-driven pages) is not reliably included
- The 404 page is excluded
- Personalization triggers are stripped
- Form submissions and configuration are excluded
- Membership data is excluded
- App data and third-party widget state are excluded
- E-commerce store data is excluded

> [!WARNING]
> **Unpublished changes are lost.** Duda's export is the live published site, not the editor draft. If stakeholders approved content in the editor but never hit publish, that content is not in the ZIP. Verify publication status before running the export. ([support.duda.co](https://support.duda.co/hc/en-us/articles/26519246857495-Site-Export?utm_source=openai))

### 2. Partner REST API

Duda's Partner API is REST-based with JSON responses. It covers site management endpoints — creating sites, managing accounts, pushing content to the Content Library, handling collections. It is not a page content API, but it's still valuable for migration.

Useful endpoints:
- **Content Library** — business data, locations, site text, logos, and site images ([developer.duda.co](https://developer.duda.co/docs/instant-websites))
- **Pages API** — page UUID, regular vs. dynamic type, path, SEO fields, custom header HTML, draft status ([developer.duda.co](https://developer.duda.co/reference/pages-v2-object?utm_source=openai))
- **Collections API** — retrieve collection data (the rows behind dynamic pages). Internal collections are capped at 100 per site, 500 rows, 50 fields, and a 2,000-character text limit per field ([developer.duda.co](https://developer.duda.co/docs/partner-api-concepts-collections))
- **Navigation API** — site navigation including language-specific navigation for multilingual sites ([developer.duda.co](https://developer.duda.co/reference/navigation-list-navigation?utm_source=openai))
- **Form data** — form submissions via API or CSV export from the UI ([support.duda.co](https://support.duda.co/hc/en-us/articles/26519262638615-Widgets-Contact-Forms?utm_source=openai))

> [!NOTE]
> **API limitation:** Duda's Pages API returns page metadata and section-level structure (`SECTION`, `element_id`, `element_source_id`), not the rendered body content. The public API is useful for inventory and partial extraction, but it is not a full-fidelity export of the editor's page model. ([developer.duda.co](https://developer.duda.co/reference/pages-v2-object?utm_source=openai))

Duda documents a hard global rate limit of **10 API calls per second**, recommending one request every 125 ms for batch jobs. Plan your extraction scripts accordingly. ([developer.duda.co](https://developer.duda.co/reference/getting-started-with-the-duda-api?utm_source=openai))

API access requires credentials restricted to the Custom plan. If you're on Agency or below, you'll rely on the static export plus scraping.

### 3. Web Scraping the Published Site

For sites where the export is incomplete or API access isn't available, scraping the live Duda site is a practical fallback. Tools like Puppeteer, Playwright, or Cheerio can capture the rendered HTML, including dynamic pages that might not appear in the static export.

Target Duda's specific CSS classes to extract content. Text widgets typically use `.dmNewParagraph`, and images are in `.dmImage`. Rows and columns use `.dmRespRow` and `.dmRespCol`. Your scraping script should parse these containers and output structured JSON ready for transformation.

Scraping is particularly useful for:
- Capturing dynamic page content the export misses
- Extracting structured data from collection-driven pages
- Getting the rendered state of personalization variants (by spoofing geo/device)

The downside: scraped HTML is flat. You'll need to parse it, identify content blocks, and extract text/images/links into a structured intermediate format before loading into Sitecore.

### Using Sitemap, robots.txt, and llms.txt as Inventories

Every published Duda site includes a default `sitemap.xml`, `robots.txt`, and `llms.txt`. Duda's sitemap includes regular pages, blog posts, dynamic page records, and e-commerce URLs. The `llms.txt` file includes regular pages, dynamic pages, blog posts, translated pages, and native store pages. ([support.duda.co](https://support.duda.co/hc/en-us/articles/26519937500055-Site-Configuration-Files?utm_source=openai))

> [!TIP]
> **Use `llms.txt` as a migration checklist.** It's one of the fastest ways to see which URLs Duda considers first-class content, especially on multilingual or dynamic-page-heavy sites. Pull the Pages API response, `sitemap.xml`, and `llms.txt`, then diff the three lists. If the counts don't reconcile, stop and investigate before building Sitecore templates against an incomplete inventory.

## Designing the Sitecore Target

Before you can import anything, Sitecore needs templates, components, and a content tree structure designed to receive the migrated content. This is the most time-consuming part of the project — and the part where most failed migrations go wrong.

### Define Data Templates

Every Duda content type needs a corresponding Sitecore Data Template. At minimum:

- **Base Page Template** — shared SEO fields (meta title, meta description, canonical URL, OG image). Other page types inherit from this.
- **Page Template** — fields for title, body content, hero image
- **Blog Post Template** — title, author, publish date, body (rich text), categories, featured image
- **Dynamic Page Template** — one per collection type, with fields matching collection columns
- **Component Templates** — hero, CTA, gallery, testimonial, FAQ, etc.

**Template inheritance guidance:** Sitecore supports multi-level template inheritance, but inheritance depth beyond 3–4 levels creates maintenance problems: field resolution becomes opaque, template editors must trace ancestors to understand what fields an item has, and Sitecore's template builder UI becomes difficult to navigate. A practical inheritance chain for migrated Duda content looks like this:

```
Standard Template (Sitecore base)
  └── Base Page Template (SEO fields: MetaTitle, MetaDescription, CanonicalUrl, OGImage)
      ├── Standard Page Template (Title, HeroImage, Body)
      ├── Blog Post Template (Title, Author, PublishDate, Body, FeaturedImage, Categories)
      └── Dynamic Item Template (fields matching collection columns 1:1)
```

Keep component templates flat — they inherit from Standard Template only, not from page templates. Do not inherit component templates from page templates; the relationship is compositional (page items reference component datasource items), not hierarchical.

Do not create a single "Page" template with one massive Rich Text field. Map Duda widgets to specific Sitecore field types:

| Duda Widget | Sitecore Field Type | Notes |
|---|---|---|
| Text Widget | Rich Text Field | Sanitize before import; strip Duda classes |
| Image Widget | Image Field | Upload to Media Library first; reference by ID |
| Button Widget | General Link Field | Distinguish internal vs. external links |
| Photo Gallery | Treelist (Image Items folder) | Create a folder item per gallery |
| Custom HTML | Multi-Line Text | Flag for manual review; likely needs refactoring |
| Video Widget | General Link or custom field | Embed URL, not binary |
| Form Widget | N/A — rebuild required | See Forms section below |

Sitecore templates support inheritance — a design advantage Duda doesn't offer. Use it, but limit inheritance chains to 3–4 levels maximum.

### Set Up the Content Tree

Sitecore XM Cloud organizes content into **Site Collections** and **Sites**. A typical content tree for a migrated Duda site:

```
/sitecore/content/
  └── MyCompany (Site Collection)
      └── MySite (Site)
          ├── Home
          ├── About
          ├── Services/
          │   ├── Service-A
          │   └── Service-B
          ├── Blog/
          │   ├── post-1
          │   └── post-2
          └── Settings/
              ├── Header
              └── Footer
```

### Configure Partial Designs and Page Designs

Sitecore XM Cloud uses **Partial Designs** (reusable fragments like headers and footers) and **Page Designs** (full page layouts combining partials with content placeholders). Map your Duda site's recurring layout sections to Partial Designs to maintain consistency across migrated pages.

One constraint worth noting: Sitecore does not allow special characters in the **item name** used in the content tree and URLs, though the **display name** can contain them. If your Duda slugs or titles contain punctuation-heavy names, separate the Sitecore item name from the display name during transformation. ([doc.sitecore.com](https://doc.sitecore.com/xp/en/users/93/sitecore-experience-platform/rename-an-item-or-page.html?utm_source=openai))

## Content Type Mapping: What Translates and What Breaks

### Blog Posts

**Extraction:** Parse the `.rss` file from Duda's site export. RSS gives you title, published date, and the full content body (as `<content:encoded>` CDATA). As noted above, author, per-post SEO meta description, OG image, and canonical tag are typically absent from the RSS output — scrape the live blog pages to capture these fields before the Duda site is decommissioned. ([support.duda.co](https://support.duda.co/hc/en-us/articles/26519232275351-Blog))

**Loading:** Create a Blog Post Data Template in Sitecore with fields for Title (Single-Line Text), Body (Rich Text), Author, Publish Date (Date), Featured Image (Image), and Categories (Multilist or Treelist). Use the GraphQL `createItem` mutation to populate items in a Blog item bucket.

**What breaks:** Duda blog URLs follow a `/blog/{slug}` pattern. Sitecore's URL structure depends on the content tree path. You'll need URL rewrites or item-level URL settings to preserve the original structure for SEO.

### Static Pages (Home, About, Services, Contact)

These are the most labor-intensive content type. Duda stores page content as a composition of visual widgets — there's no API that returns "the body text of this page" as a single field.

**Extraction:** Parse the HTML from the static export or scraping output. Use a DOM parser (Cheerio for Node.js, BeautifulSoup for Python) to extract text from Duda's widget containers (`.dmNewParagraph`, `.dmRespRow`, `.dmRespCol`).

**Loading:** Decide between two strategies:
1. **Single rich text field:** Dump the extracted HTML into one Rich Text field per page. Fast, but you lose component-level editing in Sitecore.
2. **Component-based decomposition:** Parse each Duda widget into a separate Sitecore component datasource item (Hero, Text Block, CTA, Image Gallery). More work upfront, but gives content editors full control in Sitecore Pages.

Strategy #2 is almost always worth the extra effort for sites with more than 20 pages.

### Dynamic Pages (Collection-Driven)

Duda's dynamic pages are powered by collections — tabular data stores where each row generates a page using the same template design.

**Extraction:** If you have API access, pull collection data via the Partner API's collection endpoints. If not, scrape each generated dynamic page. Internal collections are capped at 100 per site with 500 rows and 50 fields each. ([developer.duda.co](https://developer.duda.co/docs/partner-api-concepts-collections))

**Loading:** Create a Sitecore Data Template with fields matching the collection columns. Create one Sitecore item per collection row. Use item buckets if the volume exceeds ~100 items to keep the content tree manageable.

### Forms

Duda forms are widget-based and proprietary. Form submissions are stored in Duda's platform and can be exported via CSV or the API, but the form structure (fields, validation rules, submission actions) must be manually recreated.

> [!WARNING]
> **Sitecore XM Cloud does not include Sitecore Forms** (which is an XP-only module). ([doc.sitecore.com](https://doc.sitecore.com/xp/en/developers/103/sitecore-experience-platform/sitecore-forms.html)) If your Duda site relies on forms, you'll need a third-party solution on a Next.js rendering host. Common options compared:
>
> | Tool | Embed method | Webhook support | Conditional logic | GDPR controls |
> |---|---|---|---|---|
> | HubSpot Forms | Script embed or React SDK | Yes (native) | Yes | Yes (consent fields) |
> | Typeform | iframe or React embed | Yes | Yes | Yes |
> | Netlify Forms | HTML attribute | Via Zapier | Limited | Basic |
> | Custom (Next.js API route) | Native JSX | N/A (you build it) | Full | Full |
>
> For most XM Cloud migrations, HubSpot Forms or a custom Next.js API route are the two practical paths. HubSpot Forms integrates with CRM pipelines; the custom route gives full control over field mapping and submission handling.

Historical form submissions cannot be exported through the standard site export. If you need this data, extract it from Duda's dashboard or API before decommissioning. ([support.duda.co](https://support.duda.co/hc/en-us/articles/26519262638615-Widgets-Contact-Forms?utm_source=openai))

### E-Commerce (Duda Store)

Duda's built-in e-commerce supports product catalogs, checkout, and order management. **None of this exports with the site ZIP.** Store products, orders, and customer records are excluded. If you're migrating e-commerce, treat it as a separate project: export product data via CSV (capped at 500 products per CSV, 20 MB file limit, maximum 20 custom fields), and set up Sitecore's commerce solution (OrderCloud or a third-party integration) independently. Review our [ecommerce migration checklist](https://clonepartner.com/blog/blog/ecommerce-migration-checklist/) for the pre-launch and post-launch requirements.

## Loading Content Into Sitecore

The right import method depends on whether you're targeting XM Cloud (SaaS) or XP/XM on-premises.

### Authoring and Management GraphQL API (XM Cloud)

The recommended approach for XM Cloud. The Sitecore Authoring and Management API is a GraphQL-based interface supporting `createItem`, `updateItem`, and `deleteItem` mutations, plus media upload. ([doc.sitecore.com](https://doc.sitecore.com/xp/en/developers/105/sitecore-experience-manager/sitecore-authoring-and-management-graphql-api.html))

A basic content creation mutation with idempotency check:

```graphql
# Step 1: Check if item already exists before creating
query CheckItemExists {
  item(path: "/sitecore/content/MySite/Home/about-us", language: "en") {
    itemId
    name
  }
}

# Step 2: Create only if item does not exist
mutation CreateMigratedItem {
  createItem(
    input: {
      name: "about-us"
      templateId: "{TEMPLATE-GUID}"
      parent: "{PARENT-ITEM-GUID}"
      language: "en"
      fields: [
        { name: "Title", value: "About Us" }
        { name: "Body", value: "<p>Sanitized content from Duda</p>" }
        { name: "MetaDescription", value: "About our company" }
      ]
    }
  ) {
    item {
      itemId
      name
      path
    }
  }
}
```

**Idempotency pattern:** Before calling `createItem`, query by path. If the item exists, call `updateItem` with the same field values. Log both created and updated items to a CSV so you can re-run the script without duplicating content. Never assume a script run completed successfully — network timeouts during bulk imports are common, and partial runs leave the content tree in an inconsistent state.

Publishing is a separate step:

```graphql
mutation PublishPage {
  publishItem(
    input: {
      sourceDatabase: "master"
      targetDatabases: ["experienceedge"]
      rootItemId: "{ITEM_ID}"
      publishSubItems: false
      publishRelatedItems: true
      publishItemMode: SMART
      languages: ["en"]
    }
  ) {
    operationId
  }
}
```

Two details that are easy to miss:
- **`publishRelatedItems` defaults to `false`** — media or referenced items can stay unpublished if you don't explicitly opt in.
- **Publishing is asynchronous** — Sitecore exposes `publishingStatus` so you can poll for completion instead of assuming success. ([doc.sitecore.com](https://doc.sitecore.com/sai/en/developers/sitecoreai/content-modeling-and-presentation/sitecore-authoring-and-management-graphql-api/query-examples-for-management-operations.html))

**Throughput and concurrency:** Sequential GraphQL mutations against the Authoring API typically yield 200–400 items per hour, depending on field count and payload size. To increase throughput, run 3–5 parallel workers, each with its own authentication token and request queue. Exceeding ~5 concurrent workers risks hitting undocumented throttle thresholds. For migrations exceeding 2,000 items, implement exponential backoff (start at 1s, double on each retry, cap at 30s) and a dead-letter log for items that fail after 3 attempts.

Authentication requires an OAuth bearer token obtained via the Sitecore CLI (`dotnet sitecore login`) or the Identity Server.

**Media upload** is a two-step flow: request a pre-signed upload URL via `uploadMedia`, then POST the binary to the returned URL. ([doc.sitecore.com](https://doc.sitecore.com/xp/en/developers/105/sitecore-experience-manager/sitecore-authoring-and-management-graphql-api.html))

> [!TIP]
> Use the Edge Preview GraphQL endpoint (not the live Edge endpoint) for validating imports. It reads from the master database, so you can verify items before publishing to Experience Edge.

### Sitecore PowerShell Extensions (SPE) for XP/XM On-Prem

For traditional Sitecore XP or XM environments, SPE is the most powerful tool for bulk data ingestion. It runs directly inside the Sitecore context, bypassing HTTP overhead. For a 500-item import, SPE batch operations typically complete in under 5 minutes on standard hardware — significantly faster than equivalent GraphQL API calls over the network.

```powershell
$importData = Get-Content -Path "C:\migration\duda_transformed.json" | ConvertFrom-Json
$targetPath = "/sitecore/content/MySite/Home"
$failedItems = @()

foreach ($page in $importData) {
    $itemPath = "$targetPath/$($page.Slug)"
    
    try {
        # Idempotency check: update if exists, create if not
        if (Test-Path -Path $itemPath) {
            $existingItem = Get-Item -Path $itemPath
            $existingItem.Editing.BeginEdit()
            $existingItem["Title"] = $page.Title
            $existingItem["MetaDescription"] = $page.SeoDescription
            $existingItem["BodyContent"] = $page.SanitizedHtml
            $existingItem.Editing.EndEdit()
            Write-Host "Updated: $itemPath"
        } else {
            $newItem = New-Item -Path $targetPath -Name $page.Slug `
                -ItemType "/sitecore/templates/Project/Page"
            
            $newItem.Editing.BeginEdit()
            $newItem["Title"] = $page.Title
            $newItem["MetaDescription"] = $page.SeoDescription
            $newItem["BodyContent"] = $page.SanitizedHtml
            $newItem.Editing.EndEdit()
            Write-Host "Created: $itemPath"
        }
    } catch {
        Write-Warning "Failed: $itemPath — $($_.Exception.Message)"
        $failedItems += $page.Slug
    }
}

# Write dead-letter log for retry
$failedItems | Out-File "C:\migration\failed_items.txt"
Write-Host "$($failedItems.Count) items failed. See failed_items.txt."
```

If you mapped Duda widgets to individual Sitecore components, you'll also need to construct Presentation Details programmatically. Use SPE's built-in rendering commands rather than manipulating the `__Renderings` XML directly:

```powershell
$rendering = Get-Item -Path "/sitecore/layout/Renderings/Project/TextComponent"
Add-Rendering -Item $newItem -Rendering $rendering `
    -Placeholder "main" -DataSource $newItem.Paths.FullPath
```

SPE is **not available in XM Cloud**. For cloud migrations, use the GraphQL API.

### Sitecore Content Serialization for CI/CD

For repeatable environment setup, Sitecore Content Serialization (SCS) serializes templates, shared structures, and deployment-time scaffolding into version control as `.itempackage` files. Use SCS for your Sitecore templates and configuration; use the Authoring API for high-volume content loads. ([doc.sitecore.com](https://doc.sitecore.com/xp/en/developers/104/developer-tools/serialization-plugin.html?utm_source=openai))

## Media Asset Migration

Media extraction is a common failure point. Duda hosts user-uploaded assets on its CDN (typically `irp.cdn-website.com` or similar). If you migrate Duda's HTML into Sitecore without rewriting image URLs, the `<img>` tags will still point to Duda's CDN. When the Duda account is closed, every image breaks.

**The pipeline:**

1. Parse your extracted HTML, RSS, and JSON for all Duda CDN URLs.
2. Download all assets locally. Generate a hash of each file to prevent duplicate uploads.
3. Upload to the Sitecore Media Library — via the Authoring API's `uploadMedia` mutation (XM Cloud) or SPE (on-prem).
4. Update all image references in your transformed content to point to the new Sitecore Media Library paths.
5. Verify that **no** `irp.cdn-website.com` URLs remain in the Sitecore database after ingestion.

For media not captured by the export (images referenced in dynamic pages or blog posts), download them directly from the CDN URLs found in your extracted content. We cover this in detail in our guide on [how to migrate images, attachments, and embeds without broken links](https://clonepartner.com/blog/blog/how-to-migrate-images-attachments-embeds-without-broken-links/).

## HTML Sanitization

Duda injects heavy inline CSS, proprietary wrapper `<div>` tags, and platform-specific class names into its HTML. Before loading text content into Sitecore Rich Text fields, you must sanitize the markup.

Use an HTML parser to:
- Strip all `class`, `style`, and `id` attributes
- Remove Duda-specific wrapper divs
- Leave only semantic tags (`<h1>`, `<p>`, `<ul>`, `<li>`, `<strong>`, `<a>`)

Failing to sanitize will cause CSS conflicts on the Sitecore site — Duda's legacy classes will interfere with your Sitecore theme or headless frontend. Sitecore's Rich Text Editor may also strip or reformat unsanitized HTML on save, causing unpredictable content loss.

## SEO Preservation and URL Routing

URL structure changes are the single biggest SEO risk in any CMS migration. Studies of CMS migrations consistently show organic traffic losses of 20–40% in the 3–6 months following a migration with unmanaged URL changes; recovery to pre-migration baselines typically takes 3–9 months with a complete 301 redirect map in place.

1. **Crawl the live Duda site** before migration. Use Screaming Frog, `sitemap.xml`, or `llms.txt` to capture every URL, its title, meta description, canonical tag, and HTTP status.
2. **Map every old URL to a new URL.** Sitecore's URL structure derives from the content tree path by default. A Duda page at `example.com/about-us/team` maps to the Sitecore item path `/sitecore/content/Tenant/Site/Home/about-us/team`. If your new structure differs, create a 301 redirect.
3. **Implement 301 redirects** for any URL that changes. In Sitecore XP, use the IIS Rewrite Module or a custom pipeline processor. In XM Cloud, manage redirects via the Edge redirect dictionary or Next.js middleware.
4. **Preserve meta tags.** Transfer meta titles, descriptions, OG tags, and canonical URLs from Duda to the corresponding Sitecore template fields.
5. **Submit an updated sitemap** to Google Search Console post-launch.

Do not launch the Sitecore site without a 1:1 map of all legacy Duda URLs to their new counterparts.

## Multi-Language Handling

Duda supports multi-language sites by duplicating pages or using its native translation UI. It exposes language-specific navigation via the Navigation API. ([developer.duda.co](https://developer.duda.co/docs/multi-language-dynamic-pages?utm_source=openai))

Sitecore is fundamentally built around **Language Versions** on a single Item. You do not create a separate item for the Spanish page — you create a Spanish version of the English item. In SPE, use `Add-ItemLanguage`; in the GraphQL API, pass the `language` parameter in your mutation.

**Language fallback configuration is a common production failure mode.** Sitecore's `enableLanguageFallback` setting controls whether an item without a version in the requested language falls back to the default language or returns empty. In XP/XM, this is configured in `Sitecore.config`:

```xml
<setting name="LanguageFallback.EnableLanguageFallback" value="true" />
<setting name="LanguageFallback.EnableFieldFallback" value="true" />
```

In XM Cloud, language fallback is configured at the site level in the `sitecore.json` configuration. If fallback is not enabled and a migrated item has an English version but no French version, French-locale users see blank pages — not the English fallback. Enable and test language fallback before go-live on any multilingual migration.

When extracting multi-language content from Duda, group the different language URLs by their core entity. Validate each locale separately — do not assume the primary language represents the whole site.

## Common Failure Modes

**Broken internal links.** Duda's internal links use relative paths or Duda-specific URL patterns. After migration, these point to nonexistent Sitecore paths. Build a link rewriting step into your transformation pipeline.

**Missing dynamic page content.** Duda's ZIP does not export dynamic pages. If you rely only on the ZIP, entire sections disappear. Recover them from collections, sitemap data, and `llms.txt`. ([support.duda.co](https://support.duda.co/hc/en-us/articles/26519246857495-Site-Export?utm_source=openai))

**Rich text formatting loss.** Duda's HTML contains platform-specific class names and inline styles that Sitecore's Rich Text Editor may strip or reformat. Sanitize before import, not after.

**Media hotlinking.** If you don't rewrite image URLs to point to Sitecore's Media Library, images break when the Duda site is decommissioned. This is the most common post-migration failure across CMS migrations of this type.

**Language fallback not configured.** Multilingual sites where fallback is not enabled will show blank pages for locales that haven't been fully migrated yet. Configure `enableLanguageFallback` before go-live.

**Wrong publish target in scripts.** XM Cloud scripts that use `web` as the target database (copied from XP examples) will fail silently or error — XM Cloud publishes to `experienceedge`. XP scripts that target `experienceedge` will fail because that target doesn't exist on-prem. Verify target database names match your environment before running bulk publishes.

**Form data loss.** Historical form submissions cannot be exported through the site export. Extract them from Duda's dashboard or API before decommissioning.

**Unpublished changes lost.** Duda's export captures the live site only. Content approved in the editor but never published won't be in the export. ([support.duda.co](https://support.duda.co/hc/en-us/articles/26519246857495-Site-Export?utm_source=openai))

**Rendered widgets mistaken for portable content.** Some Duda widgets export visually but depend on platform services (OpenTable, vcita, etc.) that stop working off-platform. Identify these dependencies early. ([support.duda.co](https://support.duda.co/hc/en-us/articles/26519246857495-Site-Export?utm_source=openai))

## When Not to Migrate to Sitecore

Sitecore is not the right target for every team leaving Duda. Be honest about these trade-offs:

- **Budget:** Sitecore XM Cloud licensing starts significantly higher than Duda. Add hosting costs for the Next.js rendering host, development costs for custom components, and ongoing Sitecore developer rates.
- **Team capability:** Sitecore requires .NET/C# knowledge for XP, or Next.js/React for XM Cloud. If your team is non-technical, the operational overhead is substantial.
- **Site complexity:** If your Duda site is a 10-page brochure site, Sitecore is overkill. Consider WordPress, Webflow (though review [how to export data from Webflow](https://clonepartner.com/blog/blog/how-to-export-data-from-webflow-methods-api-limits-portability/) before committing), or another mid-tier CMS.
- **Timeline:** A Duda-to-Sitecore migration for a 50-page site with blog and dynamic content takes 4–8 weeks minimum with an experienced team. The breakdown is roughly: content audit and Sitecore template design (1–2 weeks), Next.js component development (1–2 weeks), ETL pipeline scripting and testing (1 week), content validation and QA (1 week), redirect mapping and go-live (1 week). The ETL pipeline itself is the smallest time investment — template design and component development dominate.

## Sitecore Pathway: Worth Evaluating

Sitecore offers **Pathway**, an AI-powered migration tool included with Sitecore 360 licensing. It supports per-site, per-language migration with phased rollouts and uses AI to propose content mappings while keeping human review in the loop. Pathway is most useful for migrations from well-structured CMS platforms (Sitecore XP to XM Cloud, or structured CMSes with defined content types). Its value for Duda migrations is more limited because Duda's flat HTML output gives AI mapping tools less structure to work with than, say, a Contentful or Drupal export. If you're already committed to Sitecore 360 licensing, request a Pathway evaluation before building custom scripts — it may handle a portion of the transformation work, but validate its output against Duda's widget model specifically before committing.

## Putting It All Together

Duda-to-Sitecore is not a lift-and-shift. It's a content re-architecture project. The extraction from Duda is the straightforward part — flat HTML, RSS, and optionally API data. The real work is designing a Sitecore content model that takes advantage of template inheritance (capped at 3–4 levels), component-based authoring, and headless delivery rather than recreating Duda's flat page structure in a more expensive system.

A practical workflow:

1. **Inventory the source.** Pull the Duda ZIP, Pages API, Collections API, Navigation API, sitemap, `llms.txt`, blog RSS, form exports, and store CSVs. Reconcile the counts — if they don't match, stop and investigate.
2. **Design the Sitecore model.** Define Data Templates with inheritance chains no deeper than 4 levels, Page Designs, Partial Designs, and the content tree before loading anything.
3. **Resolve XP vs. XM Cloud.** Confirm your target environment and lock in your primary import tool (SPE for XP/XM, GraphQL API for XM Cloud).
4. **Build Sitecore components.** Develop the Next.js rendering components (XM Cloud) or MVC/SXA components (XP) that match your target design.
5. **Extract and transform.** Parse HTML, RSS, and API data into structured JSON. Sanitize markup. Download all media. Validate RSS fields against the full list of required fields and supplement with scraped data where RSS is incomplete.
6. **Load content.** Import media first, then content items, then backfill references. Use idempotent create-or-update patterns. Batch creates, implement retry logic with exponential backoff, and maintain a dead-letter log.
7. **Configure language fallback.** Enable `enableLanguageFallback` before go-live on any multilingual site.
8. **Validate.** Compare page-by-page. Check item counts, URL counts, and rendered output. Verify no Duda CDN URLs remain. Validate each language version separately.
9. **Set up redirects.** 301 redirect every changed URL. Verify with a post-migration crawl.
10. **Cut over.** Point DNS to the Sitecore rendering host. Monitor Search Console for crawl errors for 2–4 weeks.

If you get the content audit and Sitecore template design right, the ETL pipeline is straightforward scripting. Get them wrong, and you'll be restructuring content after launch.

> Planning a Duda to Sitecore migration? ClonePartner handles the full ETL pipeline — extraction from Duda, transformation to match your Sitecore templates, and programmatic loading via the Authoring API — so your team can focus on the Sitecore build.
>
> [Talk to us](https://cal.com/clonepartner/meet?duration=30)

## Frequently asked questions

### Can you directly import a Duda site into Sitecore?

No. There is no native connector, import tool, or migration wizard between Duda and Sitecore. Duda's site export produces static HTML/CSS/JS bundles, while Sitecore requires structured content items created via its Authoring and Management GraphQL API or Sitecore PowerShell Extensions. Every migration requires custom extraction and transformation scripts.

### What does Duda's site export leave out?

Duda's ZIP export excludes blog HTML (blog content is only in the RSS file), dynamic page content, the 404 page, store data, membership data, app/widget state, form submissions, and personalization triggers. Unpublished editor changes are also excluded.

### What is the best API for importing content into Sitecore XM Cloud?

The Sitecore Authoring and Management GraphQL API. It supports createItem, updateItem, and deleteItem mutations, plus a two-step media upload flow. Authentication uses OAuth bearer tokens obtained via the Sitecore CLI. SPE is not available in XM Cloud.

### How long does a Duda to Sitecore migration take?

For a typical 50-page site with blog and dynamic content, expect 4–8 weeks minimum with an experienced team. The bulk of the time goes into Sitecore template design, Next.js component development, and content validation — not data extraction from Duda.

### What happens to Duda CDN images during migration?

Images hosted on Duda's CDN will break once the Duda account is closed. You must download all media assets, upload them to the Sitecore Media Library, and rewrite image URLs in your extracted content before decommissioning the Duda site.
