Skip to content

Hygraph vs Sitefinity: Architecture, TCO, and Migration Guide

Compare Hygraph and Sitefinity across architecture, pricing, content modeling, and migration. A technical guide for teams evaluating headless vs. hybrid CMS.

Rishabh Makhar Rishabh Makhar · · 25 min read
Hygraph vs Sitefinity: Architecture, TCO, and Migration Guide
TALK TO AN ENGINEER

Planning a migration?

Get a free 30-min call with our engineers. We'll review your setup and map out a custom migration plan — no obligation.

Schedule a free call
  • 1,500+ migrations completed
  • Zero downtime guaranteed
  • Transparent, fixed pricing
  • Project success responsibility
  • Post-migration support included

Hygraph vs Sitefinity: Architecture, TCO, and Migration Guide

Hygraph is a GraphQL-native, cloud-only headless CMS built for API-first content delivery. Sitefinity is a .NET-based hybrid platform by Progress Software that can run coupled, decoupled, or fully headless. Choosing between them is not a feature checklist exercise — it is a decision about where you want complexity to live.

Hygraph assumes content is modeled as entities and relationships, delivered through a GraphQL API, and rendered by whatever frontend your team builds. Sitefinity assumes the CMS owns more of the stack: page structure, visual editing, personalization, and rendering coordination. Those are different operating models with different cost profiles, team shapes, and migration paths.

This guide covers architectural differences, real pricing structures, content modeling approaches, localization incompatibilities, and the exact engineering work required to migrate between them — including the rich text transformation that most migration guides skip.

Architecture: API-First vs. Hybrid Headless

The architectural gap between these two platforms shapes every downstream decision about team structure, frontend ownership, and long-term maintenance.

Hygraph Architecture

Hygraph (formerly GraphCMS) is a headless CMS built around a GraphQL-native content layer with a distinctive enterprise capability: Content Federation. There is no built-in presentation layer, no page editor, and no server-side rendering engine. It stores structured content and serves it via a single GraphQL endpoint.

Content Federation is Hygraph's standout capability. Instead of migrating all enterprise data into the CMS, Hygraph's Content Federation lets you extend your GraphQL schema with external data sources — REST APIs, databases, other CMSes — and query them as a unified graph. The frontend makes one query to Hygraph, and Hygraph resolves the downstream requests. The Growth plan excludes federation entirely; the Enterprise plan supports up to 10 remote sources. Federation queries count against your API operation quota, which matters at scale.

Infrastructure: True SaaS. You do not manage servers, databases, or load balancers. You are responsible only for the frontend application and its hosting (Vercel, Netlify, AWS).

Key constraint: Hygraph is content infrastructure, not a DXP. Native marketing tooling, analytics, experimentation, visual page building, and personalization capabilities are absent and must be delegated to external systems (Uniform, Builder.io, Ninetailed, or similar).

Sitefinity Architecture

Sitefinity was originally a traditional .NET CMS but has evolved into an API-first platform that supports three distinct deployment modes simultaneously — a key differentiator from purpose-built headless platforms that force an all-or-nothing architectural commitment:

  • Traditional (Coupled): Sitefinity manages both content and presentation. Pages are built with drag-and-drop widgets and rendered server-side in ASP.NET Core.
  • Decoupled: A separate .NET Core Renderer fetches content via APIs while Sitefinity handles the backend. The renderer can also proxy legacy MVC/Web Forms pages during phased modernization.
  • Fully Headless: Content delivered via OData REST and GraphQL APIs to any frontend, including an official Next.js SDK.

All content types in Sitefinity are exposed via OData services and can be queried, filtered by taxonomies, sorted, and paginated. Those services are automatically generated — no custom code required to expose a new content type. The Sitefinity architecture separates frontend (presentation), backend (content and administration), and data into independent layers, but Sitefinity remains a stateful application requiring session management, distributed caching, and database tuning for high availability.

As of Sitefinity 15.4.8630 (May 2026), the Next.js renderer was updated to Next.js 16 and React 19.2. Customers upgrading from earlier 15.4 versions must apply manual renderer changes — a recurring upgrade cost that should be factored into TCO.

Architecture Decision Matrix

Factor Hygraph Sitefinity
Primary API GraphQL (native) OData REST + GraphQL
Deployment Cloud-hosted SaaS only Self-hosted, Sitefinity Cloud, or hybrid
Page editing None (headless only) WYSIWYG drag-and-drop + headless
Content Federation Built-in (up to 10 remote sources, Enterprise only) Requires custom integration
Personalization External tooling required Native (Sitefinity Insight, licensed separately)
Tech stack dependency Stack-agnostic frontends .NET ecosystem (backend)
Compliance SOC 2 Type 2, GDPR, multi-region residency SOC 2, GDPR, self-hosted option for full control
Locale model Field-level variants on a single record Separate content items per culture/URL
Warning

"Both support headless" is not the same as "both are architected the same way." With Hygraph, the API is the product surface. With Sitefinity, headless sits inside a broader CMS/DXP model that still owns page structure, workflows, and renderer coordination. That difference shows up in team shape, migration scope, and long-term maintenance cost.

Content Modeling: GraphQL Types vs. Dynamic Modules

How each platform structures content directly impacts migration complexity and developer experience.

Hygraph uses a schema-first approach where content types are defined as GraphQL types with strong typing throughout. The schema builder offers 15+ field types with union/polymorphic relations and nested components as first-class GraphQL types. You define models, add fields, and the API is auto-generated with deep querying. Webhooks and UI extensions handle extensibility — if you need custom business logic on publish, a webhook triggers a serverless function (AWS Lambda, Vercel Edge). The CMS stays isolated from your custom code.

Sitefinity uses dynamic modules — content types configured through the admin UI or code-first via .NET. Fields map to SQL Server columns. Taxonomies (flat and hierarchical) are a built-in first-class concept. Sitefinity development ties into the Microsoft ecosystem: backend developers write C# to create custom widgets, extend the data model via the Fluent API or Native API, or integrate third-party services through event handlers. Frontend developers build widgets using ASP.NET Core MVC patterns with ViewComponents.

Field-Type Mapping Reference

This table is the foundation of any Sitefinity-to-Hygraph schema migration. There is no automated tool that handles this translation — it requires deliberate design decisions at each row.

Sitefinity Field Type Hygraph Equivalent Migration Notes
ShortText Single Line Text Direct mapping; check max-length constraints
LongText / HTML Rich Text (AST) Requires custom HTML-to-AST parser. Highest complexity.
Choices (flat) Enumeration Map option values manually; order may differ
Choices (hierarchical) Model with self-relation Cannot be represented as a flat enum
RelatedData (single) Reference (one-to-one) Target model must be migrated first; map IDs
RelatedData (multiple) Reference (one-to-many) Same dependency ordering applies
Media (image) Asset (image) Re-upload required; capture new Asset ID for remapping
Media (document) Asset (document) Same as above
Classification / Taxonomy Enumeration or relational model See localization section for per-culture complexity
Address Component (nested model) Hygraph has no native address field; model as component
DateTime Date & Time Direct mapping; verify timezone handling
Number Int or Float Choose based on precision requirements
Yes/No Boolean Direct mapping
GeoLocation Custom (JSON field) No native geo field; use JSON or String + parse at frontend

Note on Choices (hierarchical): Sitefinity supports nested category trees natively. Hygraph Taxonomies (added 2025–2026) support hierarchical classification, but the data model differs from Sitefinity's relational category approach. Evaluate whether Hygraph Taxonomies meet your requirements before mapping hierarchical Choices to a self-referential model.

Warning

Rich text fields are the most common source of migration pain. Sitefinity stores rich text as HTML with embedded widget references. Hygraph stores rich text as an Abstract Syntax Tree (AST) in a custom JSON format. Every rich text field requires transformation — there is no shortcut, and no off-the-shelf converter handles Sitefinity's embedded widget markup.

Editorial Experience: Marketers vs. Developers

Hygraph is developer-first. Editors fill out structured forms shaped by schema decisions that developers make. There is no concept of a "page" unless you explicitly model a Page type with components for SEO, routing, and layout. This makes content highly reusable across web, mobile, and digital displays, but requires editors to think in abstract data structures rather than visual layouts. When content models become complex — nested components, multiple locales, many reference fields — editors frequently report feeling overwhelmed by the interface.

Sitefinity excels at visual page building. It ships with in-context editing, a drag-and-drop page builder, built-in A/B testing through Sitefinity Insight, workflow approvals, and API-driven personalization — all without third-party tools. Users frequently highlight its intuitive interface as accessible to non-technical editors.

If your editorial team consists of marketers who need to build pages without developer support, Sitefinity is the stronger choice. If your content is structured data consumed by developer-built frontends, Hygraph's API-first model is cleaner. Replicating Sitefinity's visual, personalized editing experience in Hygraph requires integrating third-party tools (Uniform, Builder.io, or Ninetailed) and significant custom frontend development — costs that belong in your TCO estimate, not your feature comparison.

Info

Hygraph's reliance on GraphQL means your frontend team needs strong GraphQL expertise. Over-fetching is eliminated, but complex queries with deep relations can hit API complexity limits. Hygraph enforces query complexity scoring internally; queries exceeding the complexity threshold return a 400 error. The specific threshold is not publicly documented but triggers on queries with more than 3–4 levels of nested relations with large result sets. Structure queries to fetch only required fields and use fragments to manage complexity.

TCO: Where the Cost Actually Lands

TCO is not license alone. The bigger drivers are license shape, infrastructure ownership, frontend responsibility, integration limits, and upgrade surface.

Platform and License Shape

Hygraph publishes hard platform ceilings that directly affect architecture decisions:

  • Hobby (free): 3 seats, 2 locales, 500K API calls/month (hard-blocked at cap, no overage). Not viable for production.
  • Growth ($199/month): 10 seats, 10,000 entries, 1M API operations/month, 3 locales, automatic overage billing per block of additional API operations and per GB of asset traffic. Content Federation excluded.
  • Scale ($500/month, where listed): Higher entry and API limits; check current public pricing as tiers have shifted.
  • Enterprise (custom): Dedicated support, custom SLAs (up to 99.95% uptime), SSO, multitenancy, 100+ seats, 20+ roles, 60–80+ locales, 50M+ API calls/month, 1M+ content entries, 365-day version retention, up to 10 remote sources (Content Federation), scheduled publishing, audit logs, and dedicated infrastructure.

API call metering applies per environment, aggregated across staging and production. Public usage counters refresh once daily, not in real time — which creates visibility gaps during bulk migrations or traffic spikes. CDN cache hits are excluded from metering; uncached requests are not.

Sitefinity uses a different buying model. Pricing is custom and not publicly listed. Deployments are priced per production domain or server for self-hosted, or as a subscription for Sitefinity Cloud. Based on partner and analyst disclosures, mid-market self-hosted licenses typically fall in the $10,000–$50,000+ annual range, though enterprise agreements with multi-site and Insight licensing can exceed this significantly. Sitefinity Insight (analytics, personalization, A/B testing) is licensed separately by contact-profile volume.

Key overage risk for Hygraph: High-traffic sites burn through included operations quickly, and Content Federation queries count against the operation limit. Budget for overages from the start if you are on Growth tier.

Key cost risk for Sitefinity: No per-API-call metering means more predictable licensing costs, but self-hosted infrastructure (Windows Server, SQL Server, Redis, Elasticsearch on Azure) adds substantial monthly spend that Sitefinity Cloud bakes into the subscription at a premium.

Infrastructure and Operations

Hygraph pushes the operational surface into the managed platform — you only pay to host your frontend (Vercel, Netlify, AWS S3/CloudFront), which is generally modest relative to CMS infrastructure.

Sitefinity's system requirements for self-hosted deployment center on .NET Framework 4.8 or .NET SDK 10.0 (for the decoupled ASP.NET Core renderer), Windows/IIS, and SQL Server or Azure SQL. Running these components in a high-availability configuration in Azure typically requires multiple VM tiers, managed SQL, Redis Cache, and Elasticsearch or Azure Search — infrastructure costs typical of a legacy .NET estate that should be line-itemed in any comparison.

Frontend and Experience-Layer Cost

This is where many evaluations go wrong. Hygraph does not provide a page-building DXP. You own the rendering system, preview behavior, component library, and whatever editor-facing abstractions sit between raw structured content and a publishable page. Higher initial development cost is intrinsic — as with any headless platform, you must build the entire presentation layer, routing logic, and preview environments.

Sitefinity productizes more of that surface: built-in templates and widgets reduce initial development cost for standard web projects. The trade-off is that modernization inside Sitefinity still requires manual work. Progress's CLI migration tool handles page and template structure and built-in widgets that have equivalents, but custom widgets need manual reimplementation.

3-Year TCO Comparison (Mid-Market Estimate)

Cost component Hygraph (Growth → Enterprise) Sitefinity (Cloud or Self-Hosted)
Annual licensing $2,400–$50,000+ $10,000–$50,000+ (estimated; no public list)
Hosting/infrastructure Included in SaaS; frontend only (~$1,200–$6,000/yr) Self-managed infra or Sitefinity Cloud (additional)
Implementation Lower infrastructure, higher frontend build cost Higher infrastructure, lower initial frontend build
Ongoing dev maintenance Frontend only; no backend CMS upgrades Frontend + backend + CMS version upgrades
Overage risk High (API call metering, per-GB asset traffic) Low (no API metering)
Marketing tool add-ons External tools required (Uniform, Builder.io: $12,000–$60,000+/yr) Built-in (Insight, personalization, A/B testing)
Upgrade labor Schema migrations; no infrastructure patching Manual renderer updates per CMS version; DB migrations

Migration Complexity by Project Size

Project size Content types Asset volume Locales Estimated migration effort
Small < 10 < 5,000 1–2 4–8 engineer-weeks
Medium 10–30 5,000–50,000 2–5 10–20 engineer-weeks
Large 30+ 50,000+ 5+ 25–50+ engineer-weeks

These estimates assume custom scripting, rich text transformation, asset re-upload, and redirect mapping. Each additional locale adds approximately 15–25% to migration effort due to the structural incompatibility between Sitefinity's per-culture content items and Hygraph's field-level locale variants (covered in detail below).

Upgrade Surface

Sitefinity's phased modernization strength comes with upgrade costs. Sitefinity 15.4.8630 (May 2026) moved the Next.js renderer to Next.js 16 and React 19.2, requiring manual renderer changes for customers upgrading from earlier 15.4 versions. Evaluate your internal capacity to absorb these upgrades annually.

Hygraph's upgrade gotcha is different: when you clone environments, asset URLs change, so any hardcoded asset references in source code or external systems need remediation. These are precisely the costs that rarely appear in first-pass ROI decks.

Localization: A Structural Incompatibility

This topic receives one sentence in most comparison guides. It deserves its own section because it is a primary driver of migration complexity and post-launch bugs.

How Each Platform Handles Localization

Sitefinity treats localization as separate content items per culture. A news article in English and German exists as two distinct database records with separate URLs (e.g., /en/news/article-title and /de/news/article-title). Culture variants can have different field values, different publication states, and different workflow stages independently. Taxonomies and related content are also culture-aware, meaning a related item in one language may or may not have a counterpart in another.

Hygraph treats localization as field-level variants on a single record. One Article record contains title in English, title in German, body in English, body in German — all as variants of the same record, queryable by locale parameter. Non-localized fields (slugs, numeric IDs, boolean flags) are shared across all locales on the same record.

Localization Migration Mapping Table

Sitefinity Behavior Hygraph Equivalent Migration Implication
Separate record per culture Single record, field-level locale variants N Sitefinity records (one per language) collapse to 1 Hygraph record with N locale variants
Separate URL per culture Locale-prefixed routing in frontend Frontend must implement locale routing; CMS no longer owns URLs
Culture-specific publication state Locale-level publish state (Hygraph supports this) Map each culture's status to Hygraph's locale publish state
Culture-specific taxonomy assignments Localized enum labels or localized relational records Design decision required: localize the label or localize the relationship
Related content per culture Shared references with localized fields on target References in Hygraph are locale-agnostic; referenced records must carry locale variants
Culture-specific media Shared asset with locale-specific alt text Alt text can be localized on the Asset record; the file itself is shared
Default/fallback culture Locale fallback configuration in Hygraph Hygraph supports fallback locale chains; configure explicitly

Key implication for migration scripting: If a Sitefinity site has 10,000 articles in three languages, that is 30,000 Sitefinity records. In Hygraph, that is 10,000 records with three locale variants each. Your migration script must aggregate Sitefinity's culture-specific records by their canonical identifier, then construct a single Hygraph create-mutation with all locale variants populated. Missing this aggregation step results in duplicate records per language.

URL structure: Sitefinity generates culture-specific URLs automatically based on site configuration. After migration, your frontend must implement locale routing (e.g., Next.js i18n configuration, or manual locale prefix routing). Redirect mapping must account for every culture's URL pattern — not just the default language.

When to Choose Hygraph

  • Your team is JavaScript/TypeScript-native and comfortable with GraphQL
  • You need Content Federation across multiple data sources (PIM, e-commerce, custom APIs) — and have Enterprise budget for it
  • You are building a composable stack where the CMS is strictly the content layer
  • Your editorial team is small or developer-supported
  • You want zero infrastructure management and can absorb usage-based cost variability
  • You manage content across multiple brands, regions, and data sources from a single schema

When to Choose Sitefinity

  • Your team has .NET expertise and wants to stay in the Microsoft ecosystem
  • You need built-in personalization, A/B testing, and analytics without third-party tool licensing costs
  • Marketers need to build and edit pages visually without developer involvement
  • You need on-premise or private-cloud deployment for compliance or data sovereignty
  • You want the flexibility to run headless, decoupled, or traditional rendering — and switch between modes as strategy evolves
  • You are modernizing an existing Sitefinity estate incrementally, not executing a full platform exit

Migrating from Sitefinity to Hygraph

This is the more common direction: teams moving from a monolithic or hybrid .NET CMS to a headless, API-first architecture. The hard part is not exporting records — it is translating one operating model into another. Sitefinity stores page behavior, widget composition, personalization rules, and multisite configuration inside the platform. Hygraph expects you to express all of that through schema, API consumers, and the frontend.

Step 1: Audit Your Sitefinity Instance

Before writing a single line of migration code, document everything:

  • Content types and dynamic modules — every custom type, its fields, field types, and relationships
  • Taxonomies — flat and hierarchical classifications, with culture variants
  • Media assets — total count, file types, sizes, and all locations where they are referenced (content fields, widget markup, rich text bodies)
  • Page structures — layouts, widgets, personalization rules, A/B test configurations
  • Custom code — .NET widgets, custom OData services, event handlers, API customizations
  • Integrations — Sitefinity Insight, third-party connectors, SSO providers
  • URLs and redirects — current routing patterns per culture, SEO-critical paths, canonical URLs
  • Locales and cultures — all active cultures, default culture, fallback chains, content coverage per culture (not all locales are 100% translated)

Separate reusable content from page-only layout. If you scope only "articles, pages, and assets," you will miss localization gaps, widget-embedded content, and personalization data — the expensive part of the migration.

Step 2: Extract Content from Sitefinity

Sitefinity CMS enables export and import of website content and data structure, with support for the Content Management Interoperability Services (CMIS) open standard.

You have three extraction paths:

  1. OData REST API — Best for structured content. All content types are exposed automatically. Use $filter, $orderby, and $expand to paginate. For multilingual content, include ?sf_culture=de query parameters to fetch each culture variant separately.
  2. Direct database access — Faster for bulk extraction but couples you to Sitefinity's internal schema, which changes between versions and includes complex L2 cache and versioning tables. Use only if the API is too slow for your volume, and only if you have a Sitefinity database schema expert on the team.
  3. CMIS export — Useful for full site packages, but the format requires additional transformation and does not cleanly separate content from presentation.
# Fetch English news items from Sitefinity OData API
curl -X GET "https://your-sitefinity.com/api/default/newsitems?$top=100&$skip=0&$orderby=PublicationDate desc&sf_culture=en" \
  -H "Authorization: Bearer YOUR_TOKEN"
 
# Fetch German variants of the same content type
curl -X GET "https://your-sitefinity.com/api/default/newsitems?$top=100&$skip=0&$orderby=PublicationDate desc&sf_culture=de" \
  -H "Authorization: Bearer YOUR_TOKEN"

For each content type, extract all active cultures in separate passes, then aggregate records by their SystemSourceKey or canonical ID before constructing Hygraph mutations.

Step 3: Design the Target Schema in Hygraph

Do not attempt a 1:1 migration of Sitefinity's SQL tables to Hygraph. Design a new, channel-agnostic GraphQL schema. Migrating to Hygraph involves two distinct phases: rebuilding your schema, then importing your content. Hygraph provides the Management SDK and Content API to handle both programmatically, plus a UI-based option for schema creation.

Use the field-type mapping table from the Content Modeling section above as your starting point. Key design decisions:

  • Sitefinity dynamic modules → Hygraph models
  • Sitefinity flat Classifications/Taxonomies → Hygraph enumerations or Taxonomy models (new in 2025–2026)
  • Sitefinity hierarchical Taxonomies → Hygraph models with self-referential relations or Hygraph Taxonomy (evaluate fit)
  • Sitefinity RelatedData fields → Hygraph reference fields (one-to-one, one-to-many, or polymorphic)
  • Sitefinity HTML rich text → Hygraph Rich Text AST (requires custom parser; see Step 5)
  • Sitefinity page layouts → Hygraph component-based structure (e.g., a ModularBlock union type)
  • Sitefinity image/document fields → Hygraph asset fields (re-upload required; see Step 4)
  • Sitefinity culture-specific records → Hygraph localized field variants on a single record (aggregation required)

Use the Management SDK so schema changes are repeatable and version-controlled. Use dryRun() before executing to catch conflicts.

import { Client } from '@hygraph/management-sdk';
 
const client = new Client({
  authToken: process.env.HYGRAPH_PAT,
  endpoint: process.env.HYGRAPH_ENDPOINT,
  name: 'sitefinity-to-hygraph-schema-v1'
});
 
// Inspect planned changes before committing
const plannedChanges = await client.dryRun();
console.log(JSON.stringify(plannedChanges, null, 2));
 
// Only execute after review
// await client.run();

If you do not normalize the schema — for example, using String and JSON fields to represent data without transformation — you avoid some upfront complexity but lose the ability to filter, sort, and query those fields through the GraphQL API. Plan to normalize; it pays off in every subsequent API consumer you build.

Step 4: Migrate Dependencies First, Then Content

Migrate assets and categories first because they have no dependencies. Maintain an ID mapping object for each content type migrated, so you can resolve relationships when importing dependent records.

The dependency ordering is mandatory:

  1. Assets — Download from Sitefinity's /api/default/images and /api/default/documents endpoints, upload to Hygraph via the /upload endpoint, capture new Asset IDs, and build a { sitefinityAssetId: hygraphAssetId } map
  2. Taxonomies and enumerations
  3. Independent models (authors, categories, tags)
  4. Dependent models (articles, pages — with references resolved via the ID map)
  5. Locale variants — After creating the base record in the default locale, use updateArticle mutations with localizations to populate each additional locale variant
# Create base record in default locale
mutation CreateArticle($title: String!, $slug: String!, $body: RichTextAST!, $imageId: ID!) {
  createArticle(data: {
    title: $title
    slug: $slug
    body: $body
    coverImage: { connect: { id: $imageId } }
  }) {
    id
  }
}
 
# Add German locale variant
mutation UpdateArticleLocale($id: ID!, $title: String!, $body: RichTextAST!) {
  updateArticle(
    where: { id: $id }
    data: {
      localizations: {
        upsert: {
          locale: de
          data: { title: $title, body: $body }
        }
      }
    }
  ) {
    id
    localizations { locale title }
  }
}

Step 5: Handle Rich Text Conversion

This is where most migrations stall. Sitefinity stores rich text as HTML strings, often with embedded widget markup. Hygraph expects a structured AST in its custom JSON format.

Plan for this to consume 30–40% of your total migration development time. There is no off-the-shelf tool that handles Sitefinity-to-Hygraph rich text conversion, because Sitefinity's embedded widget markup (<sf:widget> tags, inline Sitefinity media references) is proprietary and must be stripped or transformed before conversion.

The conversion requires a two-stage parser:

Stage 1: Strip Sitefinity-specific markup

  • Remove or transform <sf:widget> embedded widget references
  • Resolve Sitefinity internal media URLs to external URLs (or defer to post-asset-upload remapping)
  • Clean up Sitefinity-generated class names and data attributes that have no meaning in the target system

Stage 2: Convert clean HTML to Hygraph RichTextAST

import { fromHTML } from 'hast-util-from-html';
import { toMdast } from 'hast-util-to-mdast';
 
// Hygraph RichTextAST node types
type HygraphNode =
  | { type: 'paragraph'; children: HygraphInlineNode[] }
  | { type: 'heading'; level: 1 | 2 | 3 | 4 | 5 | 6; children: HygraphInlineNode[] }
  | { type: 'bulleted-list'; children: { type: 'list-item'; children: HygraphInlineNode[] }[] }
  | { type: 'numbered-list'; children: { type: 'list-item-child'; children: HygraphInlineNode[] }[] }
  | { type: 'block-quote'; children: HygraphInlineNode[] }
  | { type: 'image'; src: string; altText?: string; width?: number; height?: number }
  | { type: 'table'; children: HygraphNode[] };
 
type HygraphInlineNode =
  | { type: 'text'; text: string; bold?: boolean; italic?: boolean; underline?: boolean; code?: boolean }
  | { type: 'link'; href: string; children: HygraphInlineNode[] };
 
type HygraphRichTextAST = { children: HygraphNode[] };
 
function stripSitefinityWidgets(html: string): string {
  // Remove Sitefinity widget tags and their content
  // Pattern varies by Sitefinity version; audit your specific markup
  return html
    .replace(/<sf:widget[^>]*>[\s\S]*?<\/sf:widget>/gi, '')
    .replace(/<\?.*?\?>/gs, '') // Remove processing instructions
    .trim();
}
 
function convertHtmlToHygraphAST(html: string): HygraphRichTextAST {
  const cleanHtml = stripSitefinityWidgets(html);
  const hast = fromHTML(cleanHtml, { fragment: true });
 
  function convertNode(node: any): HygraphNode | HygraphInlineNode | null {
    if (node.type === 'text') {
      return { type: 'text', text: node.value };
    }
    if (node.type !== 'element') return null;
 
    switch (node.tagName) {
      case 'p':
        return {
          type: 'paragraph',
          children: node.children.flatMap(convertNode).filter(Boolean) as HygraphInlineNode[]
        };
      case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6':
        return {
          type: 'heading',
          level: parseInt(node.tagName[1]) as 1|2|3|4|5|6,
          children: node.children.flatMap(convertNode).filter(Boolean) as HygraphInlineNode[]
        };
      case 'ul':
        return {
          type: 'bulleted-list',
          children: node.children
            .filter((c: any) => c.tagName === 'li')
            .map((li: any) => ({
              type: 'list-item' as const,
              children: li.children.flatMap(convertNode).filter(Boolean) as HygraphInlineNode[]
            }))
        };
      case 'ol':
        return {
          type: 'numbered-list',
          children: node.children
            .filter((c: any) => c.tagName === 'li')
            .map((li: any) => ({
              type: 'list-item-child' as const,
              children: li.children.flatMap(convertNode).filter(Boolean) as HygraphInlineNode[]
            }))
        };
      case 'blockquote':
        return {
          type: 'block-quote',
          children: node.children.flatMap(convertNode).filter(Boolean) as HygraphInlineNode[]
        };
      case 'strong': case 'b':
        return node.children.flatMap((child: any) => {
          const converted = convertNode(child);
          if (converted && converted.type === 'text') return { ...converted, bold: true };
          return converted;
        }).filter(Boolean)[0];
      case 'em': case 'i':
        return node.children.flatMap((child: any) => {
          const converted = convertNode(child);
          if (converted && converted.type === 'text') return { ...converted, italic: true };
          return converted;
        }).filter(Boolean)[0];
      case 'a':
        return {
          type: 'link',
          href: node.properties?.href || '',
          children: node.children.flatMap(convertNode).filter(Boolean) as HygraphInlineNode[]
        };
      case 'img':
        return {
          type: 'image',
          src: node.properties?.src || '',
          altText: node.properties?.alt || '',
          width: node.properties?.width ? parseInt(node.properties.width) : undefined,
          height: node.properties?.height ? parseInt(node.properties.height) : undefined
        };
      default:
        // Pass through children of unrecognized elements
        return node.children?.flatMap(convertNode).filter(Boolean)[0] || null;
    }
  }
 
  const children = hast.children
    .flatMap((node: any) => convertNode(node))
    .filter(Boolean) as HygraphNode[];
 
  return { children };
}
 
// Usage
const sitefinityHtml = `<p>Article content with <strong>formatting</strong> and <a href="/en/page">links</a>.</p>`;
const hygraphAST = convertHtmlToHygraphAST(sitefinityHtml);
// Pass hygraphAST directly as the value of a RichTextAST field in your mutation

Critical validation step: After conversion, render the AST in Hygraph's rich text renderer and visually compare output against the original Sitefinity page. Automated field-count validation does not catch rendering differences introduced by structural mismatches.

Image remapping within rich text: Images embedded inside Sitefinity rich text (not attached as media fields) must be extracted, re-uploaded to Hygraph as assets, and their src values replaced with Hygraph CDN URLs before final import. This requires a two-pass approach: first pass extracts and uploads images; second pass completes the AST conversion with correct URLs.

Info

Rate limiting applies to Hygraph mutations. Your migration script must implement exponential backoff and batching to avoid HTTP 429 responses during bulk ingestion. Hygraph's public usage counters refresh once daily, not in real time — build your own request counters for observability during bulk loads.

async function mutateWithBackoff<T>(
  mutationFn: () => Promise<T>,
  maxRetries = 5
): Promise<T> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await mutationFn();
    } catch (error: any) {
      if (error?.response?.status === 429 && attempt < maxRetries - 1) {
        const delay = Math.pow(2, attempt) * 1000 + Math.random() * 500;
        console.log(`Rate limited. Retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`);
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error;
      }
    }
  }
  throw new Error('Max retries exceeded');
}

Step 6: Validate, Redirect, Launch

  • Content validation: Compare record counts per content type, per locale. Spot-check 5–10% of records for field accuracy, relationship resolution, and rich text rendering. Automated checks should verify: total record count matches, all referenced assets resolve, all locale variants are populated for multilingual records, no orphaned records with null required fields.
  • URL redirects: Map every Sitefinity URL to its new equivalent, for every active culture. Sitefinity uses ASP.NET-style routing with culture prefixes; your new frontend will use different patterns. A missed redirect on a high-traffic page can cause measurable organic traffic loss and a months-long SEO recovery within days of launch. Use server-side redirects (301), not client-side.
  • SEO preservation: Plan for SEO from the start. Content delivered through a new frontend requires server-side rendering (SSR) or prerendering (SSG) — client-side-only rendering may not be indexed reliably by all crawlers. Next.js with SSR or ISR is the most common pattern for Hygraph-powered frontends.
  • Asset CDN cutover: Ensure all asset URLs in content point to Hygraph's CDN, not the old Sitefinity domain. Run a full-text search across migrated content for old domain references before launch.
  • Parallel environments: Run both systems simultaneously during validation. Content authors should sign off on their content in the new system before the old one goes offline. Define a hard cutover date and communicate a content freeze window.

Recovery Procedures for Failed Batch Migrations

Hygraph mutations are not transactional — a batch failure at record 4,000 of 6,000 leaves a partial import. Build your migration scripts with these safeguards:

  1. Write a migration log: Record the Hygraph ID and Sitefinity source ID for every successfully created record. If the run fails, you can resume from where it stopped rather than re-running from the beginning.
  2. Idempotent mutations: Use Sitefinity's SystemSourceKey (or a derived slug) as an external ID. Before creating a record, query Hygraph for an existing record with that identifier. If it exists, skip or update rather than creating a duplicate.
  3. Rollback by deletion: Hygraph's Content API supports batch deletion. If a migration run produces corrupt or incomplete data, delete records with that migration run's identifier and re-run the affected batch.
  4. Staging environment first: Run the full migration against a Hygraph staging environment before touching production. Validate completely, then replicate the script against production with a content freeze in place.

Consider Federation to Reduce Migration Scope

Hygraph Remote Sources can pull external REST or GraphQL data into the Hygraph API at runtime (Enterprise tier required). This is useful when a system of record should stay in place for phase one, reducing the scope of a big-bang migration. It is not a substitute for recreating Sitefinity page composition, workflows, or personalization behavior — but it can defer some data movement and lower the risk of the initial cutover.

Migrating from Hygraph to Sitefinity

Less common, but it happens — typically when organizations need built-in marketing tools, personalization at scale, or on-premise deployment that Hygraph's cloud-only model cannot satisfy.

Export content from Hygraph using the Content API — the GraphQL API lets you pull every record with its full field set and all locale variants in a single query per type.

query ExportArticles($locale: Locale!, $skip: Int!) {
  articles(locales: [$locale, en], first: 100, skip: $skip) {
    id
    title
    slug
    body { raw }
    coverImage { url }
    publishedAt
  }
}

The harder part is mapping Hygraph's structured, API-only content into Sitefinity's page-centric model. If your Sitefinity implementation will be headless, the mapping is straightforward. If you are using Sitefinity's page builder, you need to decide how Hygraph content maps to pages, widgets, and content blocks — a design exercise that must precede any data movement.

OData REST services and the C# REST SDK are available for importing data into Sitefinity. To sync data from external systems, use the SystemSourceKey property that every Sitefinity content type carries — it is a string field (up to 255 characters) where you can store the Hygraph record ID and use it to identify records during subsequent sync operations or rollback.

Locale reversal: Hygraph's single-record, field-level locale model must be exploded back into Sitefinity's per-culture content items. One Hygraph record with three locale variants becomes three Sitefinity records, each posted to the API with the appropriate sf_culture parameter. This is the inverse of the aggregation step described in the Sitefinity-to-Hygraph path.

Common Migration Pitfalls

  1. Underestimating rich text complexity. This single transformation typically consumes 30–40% of total migration development time. Budget it explicitly.

  2. Ignoring asset dependencies. Assets must be migrated and verified before any content that references them. Broken asset references are the most common post-migration bug, especially for images embedded in rich text rather than attached as media fields.

  3. Skipping URL redirect mapping per culture. URL structures change when migrating to a new CMS, and every culture has its own URL pattern. A missed redirect on a high-traffic localized URL means lost organic traffic from that market.

  4. Not accounting for localization structural differences. Sitefinity's per-culture records and Hygraph's field-level locale variants are not 1:1. Missing the aggregation step creates duplicate records; missing the locale-populate step creates records with empty locale variants that silently fall back to the default language.

  5. Forgetting workflow and permissions. Content stages (draft, review, published) and role-based access need to be rebuilt in the target platform. Neither platform exports these automatically, and Hygraph's permission model (role → model → field → permission) is more granular than Sitefinity's default setup.

  6. Hardcoded asset URLs after environment cloning. In Hygraph, cloned environments generate new asset URLs. Catch this in testing — not on launch day.

  7. No idempotency in migration scripts. A script that cannot resume from a partial failure will force you to re-run the entire migration, potentially creating duplicate records. Build idempotency in from the start.

  8. Treating the migration as a one-time export. Content authors continue publishing in the source system during migration. Plan for a delta sync of records created or updated after the initial migration run, and define a hard content freeze window before final cutover.

The Practical Decision

The right answer is not "which CMS has more features." It is "where do we want complexity to live over the next three years?"

If you want content modeled once and consumed everywhere through GraphQL, with your team owning the rendering and experience layer, Hygraph is the cleaner fit. If you want the platform to carry more of the page, editorial, and DXP burden inside a .NET-centered system, Sitefinity is the better fit.

Migrations between these architectural paradigms are high-risk. The work that matters is schema design, locale model translation, relation preservation, asset handling, rich text transformation, and cutover control. Schema design is the migration — the hardest decisions are not about moving data, but about how to restructure your content model to take advantage of the target platform's strengths. Custom scripts with idempotency, exponential backoff, and per-record logging outperform generic migration tools for projects of any significant scale.

Frequently Asked Questions

What is the main difference between Hygraph and Sitefinity?
Hygraph is a cloud-only, GraphQL-native headless CMS focused on content infrastructure and Content Federation. Sitefinity is a .NET-based hybrid headless CMS by Progress Software that supports traditional page editing, decoupled, and fully headless modes — with built-in personalization and marketing tools.
How much does Hygraph cost compared to Sitefinity?
Hygraph starts free (Hobby) with a Growth plan at $199/month and custom Enterprise pricing. Sitefinity uses custom pricing with perpetual or subscription licensing per production domain. Sitefinity typically has higher upfront licensing costs but no per-API-call metering, while Hygraph includes hosting but charges overages on API operations.
What is the biggest risk in a Sitefinity to Hygraph migration?
Rebuilding page-centric behavior — widgets, layout, forms, personalization, and multisite rules — as structured content plus frontend logic. Exporting records is the easy part. Rich text transformation alone (HTML to Hygraph's AST format) can consume 30–40% of total migration development time.
Can Sitefinity be used as a headless CMS?
Yes. Sitefinity supports three deployment modes: traditional (coupled with WYSIWYG editing), decoupled (separate .NET Core frontend), and fully headless (content delivered via OData REST and GraphQL APIs). It also has an official Next.js SDK for headless rendering.
Can Hygraph API limits affect a bulk migration?
Yes. Uncached requests are rate-limited by plan, request sizes are capped, and public usage counters refresh once daily (not in real time). Large imports need batching, exponential backoff, and your own observability to avoid HTTP 429 errors.

More from our Blog