I have a wrapper paragraph, Layout (para_layout), which has the following fields.

enter image description here

I created the same paragraphs and field structure in a Drupal 9 website. I created the following custom migration plugin.

id: custom_para1
label: Custom paragraph migration
migration_group: custom
  - node
  plugin: d7_paragraphs_item
  bundle: para_layout
  field_left_column: field_left_column
  field_right_column: field_right_column
  plugin: 'entity_reference_revisions:paragraph'
  default_bundle: para_layout

drush mim custom_para1 shows it's processing the items, but I couldn't see any data moved to the respective field tables.

It works fine when the fields are normal text fields instead of paragraph fields.

  • You have to migrate the paragraph data first, and then migrate their host records that the node refers to in order to connect the two.
    – Kevin
    Commented Jul 7, 2021 at 18:54
  • @apaderno, so the host records will be directly inserted through SQL or can it be also migrate through migrate yml?
    – Cool
    Commented Jul 7, 2021 at 22:05
  • You migrate the host records in the same way you migrate the paragraph data. I would wait for a comment from @Kevin, since the first comment was posted by him.
    – apaderno
    Commented Jul 8, 2021 at 7:13
  • @apaderno I tried to migrate the host entry but, the result is same. Drush show processed something but nothing shows in DB(I have already migrated paragraph contents). Should I change the 'destination plugin' to something else?
    – Cool
    Commented Jul 8, 2021 at 9:54

2 Answers 2


Here is how I was able to achieve it.

Paragraph Migration:

id: fup_balance_csv_import


  field_upload: field_upload
  field_download: field_download
  field_total: field_total

  plugin: entity_reference_revisions:paragraph
  default_bundle: fup_balance

Node Migration:

id: subscription_list_csv_import


  # Paragraphs field.
      plugin: migration_lookup
      migration: fup_balance_csv_import
      source: title # Unique idetifier.
      plugin: sub_process
        - '@pseudo_field_fup_details'
        target_id: '0'
        target_revision_id: '1'

This is by using the standard Migration Process. Sometimes it might not be helpful then you need to write your own migration plugin. Below is the way to do it.

# Paragraphs Field.
    plugin: country_timezones_paragraphs
      field_1: source_field_1
      field_2: source_field_2

Migration Plugin :


namespace Drupal\countries_list_migration\Plugin\migrate\process;

use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\paragraphs\Entity\Paragraph;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;

 * Provides a countries_timezones migration plugin.
 * Usage:
 * @code
 * process:
 *   bar:
 *     plugin: country_timezones_paragraphs
 *     source: source_field_name
 * @endcode
 * @MigrateProcessPlugin(
 *   id = "country_timezones_paragraphs",
 *   handle_multiples = TRUE
 * )
class CountryTimezonesParagraphs extends ProcessPluginBase implements ContainerFactoryPluginInterface {

   * Logger service.
   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
  protected $logger;

   * Constructs a CountriesTimezones plugin.
   * @param array $configuration
   *   The plugin configuration.
   * @param string $plugin_id
   *   The plugin ID.
   * @param mixed $plugin_definition
   *   The plugin definition.
   * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger
   *   The logger service.
  public function __construct(array $configuration, $plugin_id, $plugin_definition, LoggerChannelFactoryInterface $logger) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->logger = $logger->get('countries_list_migration');

   * {@inheritdoc}
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(

   * {@inheritdoc}
  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {

  $paragraphs =[];
   if (isset($value)) {
      $paragraphs[] = $this->createCountryTimezonesParagraphsItem($value);

    return $paragraphs;

   * {@inheritdoc}
  public function multiple(): bool {
    return TRUE;

  protected function createCountryTimezonesParagraphsItem(array $item): array {

    $paragraph = Paragraph::create([

      'type' => 'country_timezones',

      'field_1' => [
        'value'  => $item['field_1'],
      'field_2' => [
        'value'  => $item['field_2'],


    return [
      'target_id' => $paragraph->id(),
      'target_revision_id' => $paragraph->getRevisionId(),


Hope its Helpful.


As I said in the comment(s), the plugins currently ensure field collections are migrated to Paragraph bundles and same for d7 Paragraphs. Field data is not moved, this is a gap you currently need to fill yourself. So you have to write YAML migration definitions to do this.

I recently did a d7 to d9 migration that had this requirement (with Field Collections in the mix too that we ported).

You will need to create migration(s) with source plugins (the SQL queries that fetch the data you need to plug in) to do the following:

  1. Migrate all actual paragraphs and their field data
  2. Migrate paragraphs that hold paragraphs (holding the field data)
  3. Migrate the records of the entities that reference the paragraphs holding the paragraphs from step 2 (use migration_lookup to find their new destination ids in D9).
  • 1
    It would be great if you could provide a sample yaml files to refer. As I already mentioned in the question the yaml that I tried is not working.
    – Cool
    Commented Jul 13, 2021 at 5:59
  • 1
    Agreed. Could anyone provide sample YAMLs of these steps just for clarification? Commented Sep 21, 2022 at 9:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.