How to Migrate from Contentful to UnfoldCMS
Half-day migration. One-time license. No per-environment fees.
Contentful's pricing model rewards your success by punishing your wallet. The starter plan looks reasonable until your project grows, and then the bills start escalating: more users, more environments, more API calls, more content types — each one another invoice line. By month nine, teams typically pay 5–10× what they signed up for. This page is for developers and teams who've decided enough is enough.
TL;DR: Migrating from Contentful to UnfoldCMS takes about half a day for typical content sites. Export Contentful with the official CLI, transform the JSON, import via UnfoldCMS REST API, set up redirects, point your frontend at the new endpoints. One-time UnfoldCMS license at $39–$799 replaces Contentful's $300–$5,000+/month bill. We offer a Migration Concierge service at $499 that handles it for you in 7 days.
Why Teams Migrate Off Contentful
Three reasons account for ~90% of Contentful migrations. None are speculative — they show up in every "we left Contentful" thread on r/webdev and Hacker News.
1. Pricing escalation. The Contentful Lite plan starts at $300/month for 5 users and 25 content types. The next tier is $2,490/month. There is no middle ground. Teams hit the Lite ceiling within 6–12 months and face a 8× price jump. This pattern repeats across hundreds of public threads.
2. Per-environment fees. Each environment (production, staging, preview) consumes API quota. Real teams burn through quotas with branch deploys and CI runs that fetch content during build. Contentful then upsells "API call packs" to bridge the gap.
3. Vendor lock-in. Migrating off Contentful is harder than migrating to Contentful. Their CLI exports JSON, but content types, references, asset URLs, and rich text need transformation before any other CMS can import them. Most teams discover this when they try to leave.
We covered the cost reality in detail in UnfoldCMS vs Contentful — including the math on when self-hosted breaks even.
What "Migrate from Contentful" Actually Involves
Five concrete steps. None require Contentful's enterprise tier or paid migration tools.
- Export your Contentful space using the official
contentful-cli - Transform the JSON — Contentful's structure differs from any other CMS, so a transformer maps content types, references, and assets to the target schema
- Import to UnfoldCMS via the REST API (single endpoint, accepts the transformed JSON)
- Migrate assets — download from Contentful's CDN, upload to your media store (S3, Cloudflare R2, or local disk)
- Update your frontend — replace Contentful Delivery API calls with UnfoldCMS REST/GraphQL calls
For a site with 200 entries and 500 assets, total work is 4–8 hours including testing.
Step-by-Step: Contentful to UnfoldCMS Migration
Step 1: Install the Contentful CLI and authenticate
npm install -g contentful-cli
contentful login
contentful space use --space-id YOUR_SPACE_ID
You'll need:
- Space ID — find it in Contentful → Settings → General settings
- Management API token — Contentful → Settings → API keys → Content management tokens → Generate
Step 2: Export the full space
contentful space export \
--space-id YOUR_SPACE_ID \
--management-token YOUR_TOKEN \
--content-file contentful-export.json \
--download-assets \
--include-drafts
This produces:
contentful-export.json— content types, entries, assets metadata, localesimages/directory — all uploaded assets
For a 500-entry space, expect a 50–200 MB export.
Step 3: Transform the JSON to UnfoldCMS format
Contentful's export structure looks like this (simplified):
{
"contentTypes": [
{ "sys": { "id": "blogPost" }, "fields": [...] }
],
"entries": [
{
"sys": { "id": "abc123", "contentType": { "sys": { "id": "blogPost" }}},
"fields": {
"title": { "en-US": "My Post" },
"body": { "en-US": "..." },
"author": { "en-US": { "sys": { "type": "Link", "id": "xyz789" }}}
}
}
]
}
UnfoldCMS expects flat post data:
{
"title": "My Post",
"slug": "my-post",
"body": "...",
"extra_attributes": { "contentful_id": "abc123", "author_id": "xyz789" }
}
A 60-line Node script handles the transform. Reference implementation:
// transform.js
import fs from 'fs';
const data = JSON.parse(fs.readFileSync('contentful-export.json', 'utf8'));
const LOCALE = 'en-US';
const posts = data.entries
.filter(e => e.sys.contentType.sys.id === 'blogPost')
.map(e => {
const f = e.fields;
return {
title: f.title?.[LOCALE] ?? 'Untitled',
slug: f.slug?.[LOCALE] ?? f.title?.[LOCALE]?.toLowerCase().replace(/\s+/g, '-'),
body: richTextToHtml(f.body?.[LOCALE]),
seo_title: f.seoTitle?.[LOCALE] ?? null,
meta_desc: f.metaDescription?.[LOCALE] ?? null,
posted_at: e.sys.createdAt,
content_type: 'post',
is_published: !e.sys.archivedAt,
extra_attributes: {
contentful_id: e.sys.id,
contentful_type: e.sys.contentType.sys.id,
},
};
});
fs.writeFileSync('unfold-import.json', JSON.stringify(posts, null, 2));
console.log(`Transformed ${posts.length} entries`);
The richTextToHtml helper uses @contentful/rich-text-html-renderer — it converts Contentful's rich-text JSON to HTML compatible with UnfoldCMS's body field.
Step 4: Import to UnfoldCMS
UnfoldCMS exposes a bulk import endpoint:
curl -X POST https://your-unfoldcms.com/api/posts/bulk-import \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d @unfold-import.json
The endpoint accepts an array of post objects and returns a result summary:
{
"imported": 487,
"skipped": 13,
"errors": [
{ "index": 42, "reason": "duplicate slug" }
]
}
For 500 entries, the import takes 30–60 seconds.
Step 5: Migrate assets
Two paths depending on volume:
Under 1,000 assets — script the upload from Contentful's CDN to your UnfoldCMS media store:
// upload-assets.js
import fs from 'fs';
import fetch from 'node-fetch';
import FormData from 'form-data';
const data = JSON.parse(fs.readFileSync('contentful-export.json', 'utf8'));
for (const asset of data.assets) {
const url = asset.fields.file['en-US'].url;
const filename = asset.fields.file['en-US'].fileName;
const imageRes = await fetch(`https:${url}`);
const buffer = Buffer.from(await imageRes.arrayBuffer());
const form = new FormData();
form.append('file', buffer, filename);
await fetch('https://your-unfoldcms.com/api/media', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.UNFOLD_TOKEN}` },
body: form,
});
console.log(`Uploaded ${filename}`);
}
Over 1,000 assets — keep them on Contentful's CDN and rewrite URLs in your post bodies. Contentful charges nothing extra for asset hosting after you stop publishing new entries.
Step 6: Set up redirects
If your URLs are changing, redirect old Contentful-driven URLs to new UnfoldCMS slugs.
UnfoldCMS has a built-in redirects table. Bulk-import a CSV via the admin or API:
from,to,status
/blog/old-slug-1,/blog/new-slug-1,301
/blog/old-slug-2,/blog/new-slug-2,301
Or via API:
curl -X POST https://your-unfoldcms.com/api/redirects/bulk \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: text/csv" \
--data-binary @redirects.csv
Step 7: Update your frontend
Replace Contentful client calls with UnfoldCMS fetches. Before:
import { createClient } from 'contentful';
const client = createClient({
space: process.env.CONTENTFUL_SPACE_ID,
accessToken: process.env.CONTENTFUL_DELIVERY_TOKEN,
});
const entries = await client.getEntries({ content_type: 'blogPost' });
After:
const res = await fetch(`${process.env.UNFOLD_API_URL}/api/blog/posts`, {
next: { revalidate: 60 },
});
const { data: entries } = await res.json();
For Next.js App Router, this lands directly in Server Components — no SDK, no token gymnastics.
What Breaks (and How to Handle It)
Real migrations always hit edge cases. Here are the common ones.
Rich text fields. Contentful's rich text is JSON with embedded entries and assets. The @contentful/rich-text-html-renderer package converts most cases to HTML, but custom embedded blocks (CTAs, code snippets, callouts) need manual mapping. Plan ~2 hours per custom block type.
Linked references. Contentful's reference fields point to other entries. After import, you need a second pass to resolve these — match the contentful_id stored in extra_attributes to the new UnfoldCMS post IDs and update the body HTML.
Locales. Contentful is locale-first. UnfoldCMS supports multiple languages through separate post entries. Decide your migration strategy: import only the primary locale first, add other locales later, OR import each locale as a sibling post linked by extra_attributes.locale_group_id.
Assets in rich text. Contentful stores asset references inline as JSON nodes. The HTML renderer converts these to <img> tags using Contentful's CDN URLs. After uploading assets to your store, run a find-replace pass on post bodies to swap CDN URLs.
Webhooks and integrations. Contentful webhooks and app integrations don't transfer — these are rebuilt in UnfoldCMS. UnfoldCMS has webhooks for content changes; map them to your existing integrations (Algolia, deploy hooks, Slack notifications).
Timeline: How Long Does Contentful Migration Take?
For typical content sites, the breakdown:
| Site size | Entries | Assets | DIY time | Concierge time |
|---|---|---|---|---|
| Small | < 100 | < 500 | 4 hours | 3 days |
| Medium | 100–500 | 500–2,000 | 1 day | 7 days |
| Large | 500–2,000 | 2,000–10,000 | 2–3 days | 14 days |
| Enterprise | > 2,000 | > 10,000 | 1–2 weeks | Custom quote |
DIY time assumes a developer who's read this guide and is comfortable writing 100 lines of Node.
Concierge time is the Migration Concierge service — $499 for sites up to 500 entries, custom quote above that. We handle the export, transform, import, asset migration, redirects, and frontend updates.
When NOT to Migrate Off Contentful
To be honest about it: Contentful is genuinely strong for some teams.
Stay on Contentful if:
- You're a large team with 20+ editors and need real-time collaboration with cursors and presence
- Your team uses Contentful's Compose, Launch, or Studio products heavily
- You have approved enterprise budget ($30k+/year is fine for you)
- Your existing integrations (Algolia, Optimizely, Salesforce) are deeply wired into Contentful's app marketplace
- You're in a regulated industry where Contentful's SOC 2 Type II + ISO 27001 + HIPAA-eligible posture matters
- You're satisfied with the pricing trajectory and don't expect to outgrow your current plan
If two or more of these apply, migration probably isn't the win you're hoping for. Read the honest comparison and decide accordingly.
What You Get After Migrating
Direct comparison for a typical mid-size content site:
| Contentful | UnfoldCMS | |
|---|---|---|
| Year 1 cost | $3,600–$30,000 | $39–$799 (one-time) + $5/mo hosting |
| Per-environment fees | Yes | No |
| Per-seat fees | Yes | No |
| API rate limits | Yes (metered) | No |
| Content modeling | Excellent | Strong (custom fields + JSON) |
| Real-time collaborative editing | Yes | No |
| Visual editing | Yes | No |
| Built-in SEO records | No (plugin) | Yes |
| Built-in redirects table | No | Yes |
| Built-in comments | No | Yes (optional) |
| Built-in forms | No | Yes |
| Source code access | No | Yes (full ownership) |
| Self-hosted option | No | Yes |
Trade-offs are real — Contentful's collaborative editing is genuinely better. But for the 80% of teams who don't need real-time multi-cursor editing, UnfoldCMS replaces Contentful for ~95% less.
FAQ
How much does it cost to migrate from Contentful? Free if you DIY (just developer time). The Migration Concierge service is $149 for a 30-min consultation + written plan, or $499 for done-for-you migration of one site up to 50 posts + media.
Will my SEO survive a Contentful to UnfoldCMS migration? Yes if you set up 301 redirects from old URLs to new ones, preserve content per URL, regenerate your sitemap, and resubmit to Google Search Console. The most common SEO mistake is shipping the migration without redirects — that drops you from page 1 to page 5 for weeks.
Can I run Contentful and UnfoldCMS in parallel during migration? Yes. Common pattern: import to UnfoldCMS, run both side-by-side for 2 weeks, run a sitemap diff to catch missing entries, then cut DNS or revalidate your frontend's API URLs to point at UnfoldCMS. Contentful stays as your safety net.
What about Contentful Apps and integrations? Contentful Apps don't transfer. Most integrations rebuild trivially in UnfoldCMS — webhooks for deploy hooks (Vercel, Netlify), API endpoints for Algolia, REST callbacks for Slack. Apps that depend on Contentful's UI (Compose, Launch) need an alternative pattern in UnfoldCMS — usually a custom field type.
How do I migrate Contentful's locales?
Three options: (1) Migrate only the primary locale and add others later, (2) Import each locale as a separate post linked by a locale_group_id in extra_attributes, or (3) Run a multi-CMS architecture during transition. Most teams pick option 2.
Will Contentful charge me during the migration? Yes — Contentful bills monthly until you delete the space or downgrade. After cutover, downgrade to the free tier (5,000 records, no API rate limit changes) and verify your frontend doesn't need it for 30 days, then delete the space.
Can I move Contentful's rich text to UnfoldCMS without losing formatting?
Yes for ~90% of cases. Use @contentful/rich-text-html-renderer to convert rich text JSON to HTML. Custom embedded entries (CTAs, code blocks, callouts) need manual mapping — plan 1–2 hours per block type.
Is the Contentful Delivery API still available during migration? Yes. Contentful keeps the CDA running until you delete the space. Run the migration with both APIs available and switch your frontend's environment variable to cut over.
Methodology
Pricing data is from Contentful's public pricing page as of May 2026. Migration time estimates are based on production migrations our team has executed for content sites of varying sizes. Step-by-step instructions reference Contentful's official CLI documentation and UnfoldCMS API docs. Edge cases (rich text, locales, references) are sourced from real migration tickets in our support queue.
Before you click — here's what you get
- Who this is for
- Teams running Contentful Team or Enterprise who are migrating to a self-hosted CMS to eliminate per-seat costs.
- What it replaces
- Contentful Team plan ($300/mo) or Enterprise ($2,000+/mo)
- What it costs
- $120 one-time Solo license — or $360 for 3 domains. Migration Concierge available at $499 done-for-you
- After signup
-
- Export your Contentful space using the Contentful CLI
- Run the transform script to convert content types to UnfoldCMS format
- Import via REST API and update your frontend fetch URLs — typically a half-day project
Migrate from Contentful to UnfoldCMS
Three paths depending on how hands-on you want to be:
- DIY — follow this guide, write the transform script yourself, ship it in a day. Free.
- Migration Starter ($149) — 30-min call where we audit your Contentful space, identify migration risks, and write you a tailored migration plan. You execute it.
- Migration Concierge ($499) — done for you. We export, transform, import, migrate assets, set up redirects, and update your frontend. 7 days, up to 500 entries. Custom quote above.
Get started with UnfoldCMS — one-time license, full source, no per-environment fees. The Core tier at $39 is enough to test the full migration on a real Contentful space.
For more context, see UnfoldCMS vs Contentful, WordPress alternative for Next.js, or the Best CMS for Next.js breakdown.
If after reading this you decide Contentful is still the right call, that's a fine answer too. The point of this page is to give you the information to choose, not to push every team toward migration.
Related: CMS for SaaS marketing sites, migrate from Sanity, or browse all migration guides.