← All playbooks
CMS migration

Contentful → Strapi

Export Contentful with the CLI, recreate content types in Strapi, then load entries and assets with a script. Keep field names close and store the Contentful id on every type so you can rerun the import. Rich text, locales, and roles do not arrive as a one-click switch.

Typical timeline15–40 business days70–160 hours active work
Statusneeds review
Source profileContentful CLI import and export docs reviewed 2026-10-31
Destination profileStrapi 5 export docs and the 2025 Contentful migration guide reviewed 2026-10-31
Documentation date
Review window
Evidence4 sources
This route needs review.Use it as a planning baseline, then verify the dated sources and test with representative data before production.
Before you migrate

Should you make this move?

Both platforms have a case. Compare what you gain with what you give up before scheduling the cutover.

Current platform

Contentful

Reasons to stay
  • Mature structured content APIs, localization, roles, and ecosystem support enterprise publishing
  • Editors get a purpose-built publishing workflow instead of raw files
Reasons to leave
  • Content-model complexity, API usage, and pricing can create significant platform dependence
  • Themes, extensions, and content models add maintenance and portability constraints
New platform

Strapi

What gets better
  • An open-source headless CMS with a writable admin, REST, GraphQL, and a readable export archive
  • Editors get a purpose-built publishing workflow instead of raw files
What gets worse
  • Self-hosting, schema code, and custom importers become your problem when you leave a SaaS CMS
  • Themes, extensions, and content models add maintenance and portability constraints
Best of the move

Strapi: An open-source headless CMS with a writable admin, REST, GraphQL, and a readable export archive. This removes a major source-side concern: Content-model complexity, API usage, and pricing can create significant platform dependence.

Worst of the move

What you lose: Mature structured content APIs, localization, roles, and ecosystem support enterprise publishing. What you inherit: Self-hosting, schema code, and custom importers become your problem when you leave a SaaS CMS.

Jump to a section
01At a glance

Know the shape of the move.

Transfer outcome12 features audited
Transfer outcome distributionClean transfer: 0, Partial transfer: 5, Manual rebuild: 5, Not transferred: 2.
Clean0
Partial5
Manual5
Lost2
Mapping route9 of 10 fields have a destination path

This timeline assumes

  • Strapi 5, self-hosted or Strapi Cloud, with admin access to create types and a transfer or API token.
  • You can run contentful-export and download assets. The strapi_lift writeup moved about 2,000 entries and 11 GB of assets in about 7 hours.
  • Contentful stays readable until Strapi no longer loads Contentful asset URLs.
  • The site already documents Contentful → Sanity, Contentful → WordPress, and Contentful → Astro if you want those destinations.
  • strapi export is for Strapi-to-Strapi. It is not the Contentful importer. Admin users and API tokens are not included in a Strapi export.
02What transfers

What survives the move.

“Partial” and “manual” are not footnotes. They are work that must be scheduled and verified.

ItemOutcomeImpactWhat happensMitigation
Content typesmanualcriticalThere is no official Contentful-to-Strapi type importer. The 2025 guide says to recreate types and keep field names close. Every type needs a contentful_id field if you use strapi_lift or a similar id map.Model types first. Add contentful_id. Do not import until the schema matches.
Entriespartialcriticalcontentful-export writes JSON. A custom script or strapi_lift creates or updates Strapi documents. strapi_lift resolves relations one level deep and wants a second pass so links land.Run the import twice. Reconcile accepted, updated, and rejected ids.
AssetspartialcriticalThe guide warns that a Contentful export can include 0-byte files when a download failed. Assets used in rich text must be uploaded and then linked.Check file sizes before import. Re-download failures. Do not leave production HTML on Contentful CDN URLs.
Rich textpartialhighContentful rich text is a document graph. Strapi may use Blocks, Markdown, or another editor. That is a transform, not a copy.Convert a sample of every content type that has rich text. Keep the raw export.
LocalesmanualcriticalContentful locales and fallbacks do not become Strapi i18n by themselves.Enable i18n on the types that need it. Import each locale on purpose.
Roles, workflows, and commentslosthighContentful roles, tasks, comments, and workflow state are not Strapi admin users. Strapi export also skips admin users and API tokens.Recreate roles in Strapi. Archive Contentful comments if you still need them.
Preview, apps, and webhooksmanualhighContentful apps, preview URLs, and webhook signing secrets stay behind.Rebuild preview and webhooks against Strapi.
IdentifierspartialcriticalJohny W. Alves described a Contentful-to-Strapi 5 move at TCGRP where documentId versus numeric id broke legacy Server Components. Treat that LinkedIn note as one team’s report.Store Contentful ids. Decide whether public URLs use documentId, slug, or a new id.
GraphQL and frontend queriesmanualcriticalThe hochzeitsplaza writeup wanted GraphQL. Field names can stay, but queries, locales, and asset URLs still change.Point a branch of the frontend at Strapi before cutover. Run the test suite.
Scheduled actions and releaseslostmediumContentful scheduled actions and releases do not import.Export the schedule. Recreate future publishes in Strapi.
Draft versus publishedpartialhighDrafts and published locales can flatten if the script only reads the published API.Export preview and published. Map status explicitly.
Hosting and operationsmanualhighYou now run Strapi, a database, and asset storage. Contentful’s SLA does not follow you.Rehearse backups and a restore before you switch the frontend.
03Field and feature mapping

Where each thing goes.

SourceDestinationMethodNotes
Content typeStrapi collection or single typemanualKeep field names close.
sys.idcontentful_idtransformRequired for safe reruns.
Entry fieldsStrapi attributestransformWatch required fields and types.
Link to entryRelationtransformResolve after both sides exist.
Link to assetMedia fieldtransformUpload first.
Rich text documentBlocks, Markdown, or custom JSONtransformConvert a sample per type.
LocaleStrapi i18n localemanualEnable i18n first.
SlugUIDtransformKeep public slugs.
WebhookStrapi webhookmanualNew secrets.
Role or workflowNo destinationunsupportedRecreate in Strapi admin.
04Before you begin

Make the move recoverable.

Backup procedure

Create the source-of-truth backup

Keep a Contentful export and asset folder you can reimport or fall back to.

  1. Run contentful-export for the space, including assets.
  2. Record content type and entry counts per locale.
  3. Hash the JSON and the asset folder.
  4. Export webhook URLs and preview settings.
  5. Keep Contentful preview working.

Proof to capture: Export hash, entry counts, and a named restore owner.

Transformation · Strapi Content-Type Builder plus a contentful_id field

Schema and id map

Give the importer a Strapi type that can find Contentful rows again.

  1. Recreate each type.
  2. Match field names where you can.
  3. Add contentful_id.
  4. Enable i18n only on types that need it.

Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.

Transformation · strapi_lift or a script you own

Entry and asset load

Create or update documents without duplicating on rerun.

  1. Upload assets and skip 0-byte files.
  2. Create entries with field values.
  3. Resolve relations on a second pass.
  4. Compare Contentful ids to Strapi documents.

Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.

05Handle with care

The things most likely to hurt.

These are operating limits. Treat every “Stop if” condition as a blocked migration, not a suggestion.

Import

Missing contentful_id

criticalpossible likelihood

The importer cannot match an existing Strapi row to the export.

Consequence
Reruns duplicate entries or skip updates.
Mitigation
Add the field on every imported type before the first run.

Stop if: A type in scope has no contentful_id.

Assets

0-byte assets

highpossible likelihood

The Contentful export folder has empty files.

Consequence
Production pages show broken images.
Mitigation
Fail the import on 0-byte files. Re-download.

Stop if: A required asset is empty after one retry.

Cutover

Frontend still queries Contentful

criticalpossible likelihood

A preview or production build still has CONTENTFUL_SPACE.

Consequence
Editors change Strapi. The site does not.
Mitigation
Search env files and CI. Run the production build against Strapi first.

Stop if: A production request still hits Contentful Delivery API.

06Precise timeline

Do the work in this order.

Estimate forOne Contentful space, a few dozen types, and a frontend you can point at Strapi
Total elapsed15–40 business days
Active work70–160 hours
BufferAdd time for rich text conversion, many locales, or a large asset library.
01
Days 1–5Inventory Contentful4–5 days
02
Days 5–8Export the space2–4 days
03
Days 8–28Schema and rehearsal import12–20 days
04
Days 28–35Freeze and switch the API3–5 days
05
Days 35–40Observe and close5–7 days
  1. Days 1–5 · inventory

    Inventory Contentful

    12–20 hours active4–5 days elapsedOwner review waiting
    • List types, locales, apps, and webhooks.
    • Decide public ids and slugs.

    Depends on: Contentful and Strapi admin access

    Stop / go checkpoint

    Export?

    Go when: The type map is signed.

    Stop when: A required locale or type is unknown.

  2. Days 5–8 · backup

    Export the space

    6–12 hours active2–4 days elapsedAsset download waiting
    • Run contentful-export.
    • Hash files.
    • Count 0-byte assets.

    Depends on: inventory

    Stop / go checkpoint

    Build types?

    Go when: The export hash and counts exist.

    Stop when: Assets failed at scale.

  3. Days 8–28 · pilot

    Schema and rehearsal import

    40–100 hours active12–20 days elapsedImport runtime waiting
    • Create Strapi types.
    • Convert rich text samples.
    • Run the importer.
    • Point a frontend branch at Strapi.

    Depends on: backup

    Stop / go checkpoint

    Cut over?

    Go when: Counts, assets, and the frontend pass.

    Stop when: A required relation or locale is wrong.

  4. Days 28–35 · cutover

    Freeze and switch the API

    8–18 hours active3–5 days elapsedFinal export waiting
    • Freeze Contentful publishes.
    • Re-export and rerun the importer.
    • Point production at Strapi.

    Depends on: pilot

    Stop / go checkpoint

    Strapi is the CMS?

    Go when: Production reads Strapi only.

    Stop when: A production page still uses Contentful.

  5. Days 35–40 · observe

    Observe and close

    6–10 hours active5–7 days elapsedPublish cycle waiting
    • Watch previews, webhooks, and asset hosts.
    • Keep Contentful readable.

    Depends on: cutover

    Stop / go checkpoint

    Close rollback?

    Go when: One publish cycle is clean.

    Stop when: Ids or assets regress.

07The point of change

Cut over with a way back.

Go live

Cutover

Make Strapi the only CMS the production frontend reads.

Recommended window: A weekday with CMS and frontend owners on call.

  1. Freeze Contentful publishing.
  2. Run a final export and importer pass.
  3. Reconcile counts and asset hosts.
  4. Switch production environment variables.
  5. Publish one known entry and confirm the live page.

Proof to capture: Production reads Strapi. Counts match. Assets do not use Contentful URLs you will cancel.

Return to safety

Rollback

Return the frontend to Contentful without losing Strapi-era edits you still want.

Deadline: Before you delete the space or drop the export.

  1. Point production back at Contentful.
  2. Export Strapi entries changed after cutover.
  3. Apply only reviewed entries back to Contentful if needed.
  4. Keep the Contentful export.

Proof to capture: Production reads Contentful again.

Rollback immediately when
  • Count mismatch
  • Broken required asset
  • Frontend still mixed between APIs
  • Locale missing
  • Preview or webhook failure
08Verification report

Prove the migration worked.

Every blocking check must pass. Capture the evidence before cleanup begins.

0%
Interactive report preview0 / 8 checks passed
PassIDCheckMethodExpected resultEvidence
V-01Blockingcontentful_id presentInspect each imported type.Every in-scope type has the field populated.Schema screenshot.
V-02BlockingEntry countsCompare Contentful and Strapi per type and locale.Counts match or variances are signed.Count sheet.
V-03BlockingFile sizes and hostsReject 0-byte files. Sample live pages.Required assets are non-empty and not on a host you will cancel.Asset report.
V-04BlockingSecond-pass linksOpen entries that have several relations.Links resolve.Relation sample.
V-05BlockingProduction APISearch env and watch network on a production build.No Contentful Delivery requests.Network log.
V-06BlockingDraft versus publishedPublish one draft and unpublish one entry.The frontend follows Strapi status.Publish log.
V-07BlockingLocale pagesOpen each published locale for a sample type.Approved locale content.Locale sample.
V-08BlockingContentful still servesKeep the space and a rollback env.You can point the frontend back.Rollback note.
09Post-migration cleanup

Remove the scaffolding safely.

Safe after: One publish cycle, no required Contentful asset hosts, and seven stable days.

  1. Archive the export, asset folder, and importer log.
  2. Remove contentful_id only after you know you will not rerun.
  3. Revoke Contentful tokens the frontend used.
  4. Keep the space readable through the retention window.
  5. Document Strapi backup and restore.
  6. Schedule the next schema and token review.
10Experiences from the field

When the plan met reality.

First-hand accounts are preferred. Vendor case studies are labeled, and every note below is an editorial paraphrase—follow the link for the full context.

First-person accountTobias Schlottke · toadle.me

Automated migration from Contentful to Strapi with strapi_liftUndated

Tobias Schlottke moved hochzeitsplaza.de off Contentful after a price increase. He wanted an open CMS with GraphQL and wrote strapi_lift because no stock importer existed. The tool reads a Contentful export, creates or updates Strapi documents using a contentful_id field on every type, uploads assets, and resolves relations one level deep so you can rerun the import. His project had about 2,000 entries and more than 11 GB of assets and took about 7 hours. He warns that Contentful exports can include 0-byte files, and that mismatched field names break the load. A matching article also appears on the Strapi blog. This is the author’s own site.

What they recommend
  • Add a contentful_id field on every imported type before the first run so reruns update instead of duplicating.
  • Inspect the asset folder for 0-byte files and keep field names close to the Contentful model.
Worth noticing
  • A custom importer plus a stable Contentful id is how they moved 2,000 entries, not a Strapi menu item.
  • Relations needed a second pass because the tool only resolves one level at a time.
Sources and evidence

Verify against the primary material.

Platform behavior changes. Check these sources and the review dates above before executing a production migration.

  1. Strapi: migrate from Contentful (2025 guide)Accessed 2026-10-31
  2. Tobias Schlottke: automated Contentful to Strapi with strapi_liftAccessed 2026-10-31
  3. Contentful: CLI import and exportAccessed 2026-10-31
  4. Strapi: data exportAccessed 2026-10-31