How to Migrate content into Drupal Paragraphs

Auteur(s) de l'article

One of the more challenging pieces of content to migrate are Paragraphs. The Paragraphs module allows you to create a content field that is essentially a bundle of fields within a Drupal content type
Example of Node composed of multiple Paragraph
Paragraphs are useful if you want multiple values that each hold more than one thing — for instance, a matching activity with several text-image pairs, or a collapsible widget with multiple pairs of heading and section text.
The migration example bellow consists of migrating multiple kind of Paragraphs and connecting those migrated paragraphs to Nodes.
This article is based on Drupal 9.2.6 using Migrate Plus 5.2.x & Migrate Tools 5.1.x

Understanding the example set up

The example are dispatched in 4 sections:
  1. Migrating some Body Text into Rich Text Paragraphs
  2. Migrating some Image(s) as Files and the Media
  3. Migrating previously Media into Gallery Paragraphs
  4. Migrating Nodes and connecting those Paragraphs

The example code required the following things:

  • Media Image Entity (image)
  • Node Entity (article) with a Paragraph field (field_content)
  • A Paragraph (rich_text) with a CKeditor field (field_body)
  • A Paragraph (gallery) with a multiple field Media (field_images)
The words in parenthesis represent the machine names of the different elements.
The module directory tree
The whole code example can be fetched from Gist on

Migrating into a Rich Text Paragraph type

Migrating into a paragraph type is very similar to migrating into a content type. You specify the source, process the fields making any required transformation, and set the destination entity and bundle. 
The following code snippet shows the source, process, and destination:
The most important part of a paragraph migration is setting the destination plugin to entity_reference_revisions:paragraph.
The other configuration that you must set is the default_bundle. The value will be the machine name of the Paragraph type you are migrating into.
You can execute the paragraph example migration with this command
drush mim articles_paragraphs_text

Migrating into a Gallery Paragraph type

First we need to create file entities for each file.
This is because Drupal treats files as File entities which have their own ID. Then Drupal treats node-file associations as entity references, referring to the file entities with their IDs.
We create the File entities in the articles_images_files.yml
Once done, you will use the same source to migrate Files into Media.
We create the Media entities in the articles_images_media.yml.
As we want to handle multiple images for the same article, the source:ids of the embedded_data is a little bit different than any others to use both the Article ID and the Image ID. Indeed, every Row used by Migrate must have a unique identifier, combining both ID make every row unique.
The resulting migration of Media
The resulting migration of Media

Migrating Media into Gallery Paragraph

Finally, you can migrate your Media entity into a Gallery Paragraph
You can execute the paragraph example migration with this command
drush mim articles_paragraphs_gallery --execute-dependencies

Migrating Nodes and connecting those Paragraphs

The node migration will serve as the host for both referenced paragraphs Rich Text and Gallery
The following snippet shows how the source, destinations, and dependencies are set:
Note that paragraphs entities are revisioned meaning when creating a reference to them, you need to provide two IDs: target_id and target_revision_id
In order to handle multiple Paragraphs into a single Node field (field_content) we will use a sub_process to deal with multiple prepared migration_lookup 
  • paragraph_gallery: Prepared lookup for previously migrated Paragraph Gallery to be attached to the field_content .
  • paragraph_text: Prepared lookup for previously migrated Paragraph Text to be attached to the field_content .
You can execute all the example migrations in cascade with the following command
drush mim articles --execute-dependencies
A migrated Node article with both Paragraphs attached
A migrated Node article with both Paragraphs attached

Bonus: Allow nullable Paragraph Lookup

As we handle multiple Paragraphs migration, we may want to allow some migration_lookup to be empty. Therefore, this is not possible by default as the sub_process plugin will fails if input is array([0 => null]).
Here is a plugin null_as_array that is intend to be used to transform migration_lookup null output (array([0 => null])) into a sub_process iterrable.
You may use this new plugin by updating the Paragraphs lookup:
# Paragraphs.
      plugin: migration_lookup
      migration: articles_paragraphs_text
      source: id
      plugin: null_as_array


For the most curious of you, here are some sources of additional information that inspired the creation of this article.
Nevin Katz (14 September 2021). How to Migrate Paragraphs from Drupal 7 to Drupal 9.
Seen on…
Mauricio Dinarte (15 May 2020). Introduction to paragraphs migrations in Drupal.
Seen on…/introduction-paragraphs-migrations
Matthew Cowgur (01 December 2021). Paragraphs migration example?
Seen on
Jigar Mehta (24 September 2021). Drupal 8/9 Migration: Migrating Files and Images (Part 3)
Seen on…/drupal-8-migration-migrating-files-images