Drupal is a registered trademark of Dries Buytaert
Protected Pages 3.0.0 Major update available for module protected_pages (3.0.0). Commerce Core 3.3.8 Minor update available for module commerce (3.3.8). Search API HTML Element Filter 1.0.7 Minor update available for module search_api_html_element_filter (1.0.7). Layout Builder Reorder 2.0.1 Minor update available for module layout_builder_reorder (2.0.1). Ban 1.1.0 Minor update available for module ban (1.1.0). Field Formatter Range 2.0.0 Major update available for module field_formatter_range (2.0.0). Field Formatter Range 8.x-1.8 Minor update available for module field_formatter_range (8.x-1.8). UI Patterns (SDC in Drupal UI) 2.0.18 Minor update available for module ui_patterns (2.0.18). Search API Solr 4.3.11 Module search_api_solr updated after 14 months of inactivity (4.3.11). Provus Mega Menu Module provus_mega_menu crossed 1,000 active installs.

This module helps migrate content from Contentful to Drupal by using the core Migrate API. It handles complex content types like rich text, embedded entries and assets, and cross-references, converting them into standard Drupal entities. The module is flexible and adaptable to different Contentful space structures, and it includes tools to assist with the migration process for various presentation modes.

Contentful Migration moves a Contentful space export into Drupal using the core Migrate API, handling the parts a YAML-only migration can't: Rich Text, embedded entries and assets, media, and cross-reference links.

It is a runtime, not a fixed content model. Every Contentful space is modelled differently, so the module executes per-space migration YAML rather than shipping one schema. The worked migrations in migrations/examples/ are templates you adapt to your space. (The same mapping YAML can optionally be generated by companion content-model analysis tooling.)

What it does

  • Rich Text AST → HTML with embedded entry/asset resolution and inline hyperlink rewriting. The contentful/rich-text library's default renderers emit Contentful-id placeholders (visible garbage) or silently drop unknown nodes. This module owns the full renderer list, resolves sys.id references to the already-migrated Drupal entities, and emits clean <drupal-entity-embed> / <drupal-media> tokens — ending in a logging catch-all that never silently empties. Inline entry-hyperlinks become links to the target entity's canonical page (carrying data-entity-uuid for Linkit-style alias-safe rewriting); asset-hyperlinks link to the migrated file's URL when the media + file modules are installed (plain-text degrade otherwise — nothing silently lost). The bundled recipes/contentful_embed/ ships the text format that renders the tokens: embedded assets on core media's media_embed, embedded-entry tokens via contrib Entity Embed (tag pre-allowed; two-step documented).
  • Assets → Media, staging files into Media entities and de-duplicating identical bytes by SHA-256 (self-contained — no migrate_file_to_media).
  • Reference fields → internal links, rewriting Contentful entry/asset reference fields to alias-safe entity: link URIs.
  • Locale flattening and a two-pass migration (entities first, bodies second) so embeds resolve against already-migrated content.
  • Multi-value Paragraph references via the idiomatic core #2890844 workaround.
  • Authorship timestampssys.createdAt/updatedAt map to created/changed with two core process plugins (worked example included).
  • Author attribution (opt-in)drush contentful:export --include-users stages the space’s members entry-shaped in users.json (exports alone carry only opaque author ids — 204/211 profiled, zero user objects); the contentful_user example imports them as blocked stubs (status: 0, no roles, no password — attribution targets, never logins) and entries resolve uid via migration_lookup. Duplicate and over-length display names are made safe deterministically at the export step, so delta re-imports never rename members. Off by default; a no-fetch static_map path is documented.
  • Repeatable / delta imports — re-export then re-import updates changed entries in place (track_changes, kernel-verified). Deletions don't propagate (a snapshot export has no tombstones) — documented honestly; this is not a sync engine.
  • A drush contentful:export command wraps the contentful-export CLI (assets included) to stage a space's JSON + asset binaries ready for migration; the management token is read from the CONTENTFUL_MANAGEMENT_TOKEN environment variable, never the process argv.
  • Decoupled conversion toolkitdrush contentful:jsonapi-map emits a machine-readable Contentful → JSON:API mapping manifest (content type → resource name, field id → JSON:API field name; pipelines it cannot classify are flagged for manual review, never guessed); the worked examples preserve each entry’s Contentful id in field_contentful_id for durable JSON:API lookup; and a query-by-query CDA → JSON:API conversion guide (modes/examples/decoupled/CONVERSION.md) covers endpoints, filters, pagination, includes, identity, rich text, and assets.

Migrated content lands in standard Drupal entities (nodes, Paragraphs, Media, menus). All three presentation modes are documented with worked reference templates in modes/examples/: decoupled (the JSON:API enable step and the embed-token contract for SPA consumers), recoupled (an editorInterfaces → Drupal widget map — grounded in 208/211 profiled exports — plus four worked display templates), and semi-decoupled (an honest pointer). One drush recipe command makes embeds render; per-space display config is derived from your export, never shipped.

Project status

Early development (1.0.x, rc). The migration runtime — including Rich Text embed and inline-hyperlink resolution — and the contentful:export Drush command are built and covered by unit and kernel tests, including real two-pass Migrate runs that resolve embeds and hyperlinks, stage assets to Media, attach translations, and render a migrated body through the shipped contentful_embed recipe. Both former roadmap items shipped in 1.0.0-beta1 (presentation-mode templates + embed recipe; asset-hyperlinks to the migrated file). Opt-in author → user mapping shipped in 1.0.0-beta2 (--include-users, blocked-stub author attribution). The decoupled conversion toolkit — jsonapi-map mapping manifest, identity-preservation pattern, CDA → JSON:API conversion guide — shipped in 1.0.0-beta3. Inline-embed resolution and reference-safety hardening shipped in 1.0.0-rc1, the first release candidate (inline-embedded entries now resolve like block embeds; stub-free migration_lookup and NULL-safe reference-revision writes). The roadmap is empty by graduation.

This project is not yet covered by Drupal's security advisory policy.

Requirements

  • Drupal 11
  • PHP 8.3+
  • Core Migrate (the only hard dependency)
  • contentful/rich-text ^4.0 (pulled in via Composer)

Paragraphs, Media, and migrate_plus are needed only when your mapping uses them — see the README.

Getting started

composer require 'drupal/contentful_migration:^1.0@beta'
drush en contentful_migration

Then stage your space and adapt the example migrations:

CONTENTFUL_MANAGEMENT_TOKEN=cfpat-… drush contentful:export --space-id=YOUR_SPACE
# edit migrations/examples/ to match your content model

Not in scope

Each exclusion is a deliberate decision, with the evidence it rests on (211 real space exports profiled):

  • Live/bidirectional sync. One-way migration only; repeatable delta imports cover the re-export case — a live two-way bridge is a different product.
  • Full edit/revision history. Exports carry only version counters — none of the 211 profiled exports contain revision snapshots (recovering history would need per-entry Management-API calls). Authorship metadata does migrate: timestamps, and opt-in author → user mapping (--include-users).
  • Roles/permissions. Present in most exports (154/211), but Contentful's policy rules don't map onto Drupal's permission model — auto-generating roles risks granting more than intended. Model roles deliberately in Drupal.
  • Webhooks, UI extensions, SSO. Contentful platform config with no safe Drupal equivalent.
  • Theme/design-system generation. Recoupled presentation consumes a provided theme.
  • Contentful CDA emulation. Evaluated and gated, not forgotten: the response envelope is reproducible, but the contract (RichText AST, Images API parametric transforms) is not — 48% of 218 profiled spaces carry RichText fields, 83% carry assets. The decision record with explicit reopen gates ships in the module (spike/cda-emulation/DECISION.md); conversion is the supported path.

Activity

Total releases
8
First release
Jun 2026
Latest release
1 month ago
Releases (12 mo)
8 ▲ from 0
Maintenance
Active

Release Timeline

Releases

Version Type Release date
1.0.0 Stable Jun 15, 2026
1.0.0-rc1 Pre-release Jun 13, 2026
1.0.0-beta3 Pre-release Jun 12, 2026
1.0.0-beta2 Pre-release Jun 4, 2026
1.0.0-beta1 Pre-release Jun 3, 2026
1.0.0-alpha2 Pre-release Jun 3, 2026
1.0.0-alpha1 Pre-release Jun 2, 2026
1.0.x-dev Dev Jun 2, 2026