Series Overview & ToC | Previous Article | Next Article - coming soon!


We executed the last field-related migrations in the previous article, but we are not done with field configuration yet. Back in article 17, we used the Migrate Skip Fields module to prevent the automatic migration from importing image and YouTube fields. Today, we are going to use Drupal recipes to create media types and manually add media reference fields where needed.

Drupal recipes

The Recipes API,introduced in Drupal 10.3, allows installing modules and themes, importing configuration and content, and altering existing configuration. Recipes can also depend on other recipes, making them composable and highly flexible. Another benefit is the potential to replace installation profiles. This not only saves time but can bring consistency across different projects. In fact, Core's standard installation profile was replicated as a set of recipes. We are going to use a couple of those recipes to add media types to our Drupal 10 project.

Drupal recipes can validate that configuration elements are in a known state before the recipe is applied. Once a recipe is applied, the imported configuration is handled by the site itself. Any updates should follow standard configuration management practices.

Take a look at the core/recipes folder in Drupal 10’s docroot to better understand the anatomy of a recipe. Each one is contained in a folder with a required recipe.yml file. They can optionally contain config and content folders to import configuration and content respectively. We could certainly write a whole series on Drupal recipes, but for now we are going to focus on what is applicable to our example project.

Technical note: Recipes are a first-class Composer project type: drupal-recipe. They can be required as any other Composer package.

Applying a Drupal recipe

Drupal Core provides a built-in script for applying recipes. Starting with Drush 13, a recipe command is available. When executed from a Drupal 10 docroot, any of these commands can be used to apply a recipe:

php core/scripts/drupal recipe /path/to/recipe_folder
drush recipe /path/to/recipe_folder

We are going to use the Drush approach since applying recipes from the host into the Docker container requires some extra arguments. For reference, refer to this documentation to apply recipes when using DDEV.

First, we will apply a recipe to use images as media entities. From the Drupal 10 folder in your host machine, run the following command:

ddev drush recipe core/recipes/image_media_type

If things go as expected, you will see a message indicating that the Image media type applied successfully. This should create an image media type, add an image field to it, and configure the default and media_library view and form modes. Visit https://migration-drupal10.ddev.site/admin/structure/media/manage/image/fields to see how things were set up.

Manage Form Display page

These changes required adding new configuration to the site. Remember to export and commit the changes as with any other configuration change. If the recipe failed to apply, refer to the troubleshooting section below.

With one recipe applied, we will now do another that will allow YouTube videos to be embedded. From the Drupal 10 folder in your host machine, run the following command:

ddev drush recipe core/recipes/remote_video_media_type

When applied, the recipe will create a remote_video media type, add a plain text field to it, and configure the default and media_library view and form modes. The text field is used to store a link to the remote video URL. YouTube and Vimeo are supported out of the box. Visit https://migration-drupal10.ddev.site/admin/structure/media/manage/remote_video/fields to see how things were set up. As you would expect, the site configuration has been updated. Export the changes and commit them to the repository.

With media types already created, we are going to manually add media reference fields where appropriate. Per the site audit document referenced in article 3, these are the fields to add:

  1. In the Article content type, add a media reference field to images.
  2. In the Venue content type, add a media reference field to images.
  3. In the Session content type, add a media reference field to remote videos. Add this field to the Resources group.

This is a site building exercise readers should be able to do on their own. Refer to the code repository for the final configuration.

Troubleshooting recipe application

Recipes verify that included configuration does not exist or that existing configuration matches the recipe definition exactly. This check is also performed for dependent recipes.

In the case of existing configuration, the check is based on machine names. Any variation will prevent the recipe from applying. For example, the image_media_type recipe depends on the large image style. If we update its label from Large (480×480) to Large 480×480 px, the following error will be triggered when trying to apply the recipe:

In ConfigConfigurator.php line 47:


 The configuration 'image.style.large' exists already and does not match the recipe's configuration

Our label change might not be meaningful, but what if we had made changes to the image effects used in that image style? We certainly would not want to lose those customizations! As a general rule, recipes would not overwrite any configuration element if its current value differs from the recipe definition. Instead, a validation error is yielded. This might seem limiting. What is the point of using recipes then?

Remember that recipes are meant to be starting points. You can install a site from recipes. When there is no existing configuration, no conflicts can arise. That being said, applying recipes on existing sites is a valid and useful feature. In addition to those provided by Core, there are contributed recipes that you can use on your projects today.

Note: As of Drupal 10.4, recipes can opt out of strict comparisons with existing config. At the time of publishing, a strict comparison is still the default. Changing to making the configuration comparison lenient by default is being discussed in this issue.

Should we use recipes in migration projects?

It depends on your project requirements and the desired content model.

In our case, our Drupal 7 project did not use the Media module, but we still wanted to use media entities in Drupal 10. Recipes are available for creating media types modeled after the standard installation profile. No need to reinvent the wheel here!

Even so, put on your site builder hat as we take a deeper look at the implications of using these recipes.

Our example Drupal 7 project is arguably very simple. It is based on the standard installation profile and there are not many customizations. No custom image styles or text formats, nor changes to those provided out of the box. Only one custom role (Editor) and no WYSIWYG configuration. We could definitely use many Core recipes modeled after the standard installation profile, but should we?

For the sake of the example, consider image styles. Drupal Core provides a migration for image styles from Drupal 7. If we were to use it, the migration will overwrite any image style to match its Drupal 7 definition. In the case of Core image styles, Drupal 10 uses a WebP converter. That feature did not exist before. Since we are not customizing the Core image styles, and we do not have any custom one, for our example project it is better not to migrate image styles. That way, we take advantage of the optimizations that come with WebP in Drupal 10.

If you have custom image styles, use the upgrade_d7_image_styles migration from the ref_migrations folder. In this case, the image style migration needs to happen after applying the image_media_type recipe. Otherwise, updates to existing image styles will make the configuration validation fail and the recipe would not be applied. While this might seem like a chicken and egg problem, good planning goes a long way into determining the correct order to apply recipes, migrate configuration, and do manual configuration changes.

Technical note: Most source plugins for upgrading from Drupal 7 connect directly to the database to fetch data. Drupal 7 allowed some configuration to be defined in code, either by modules or features. When that configuration is in its default state, only the code representation exists. The database would not hold a reference to it. That is the case for the image styles in our example. Checking the status of the upgrade_d7_image_styles reveals there are no records to migrate, but we know that image styles do exist in Drupal 7. Those present only exist in code. For them to exist in the database, you would need to overwrite them. This also happens with views, field groups, and other configurations that can be represented in code. Take this into account if a migration reports no records to migrate or the total count is lower than expected.

To answer the question posed at the start of this section, it is ok to use recipes when they can be cleanly applied. If subsequent migrations might overwrite configuration elements created by the recipes, make sure the new configuration is truly what you want. If we export and commit configuration changes after every step, git should show us if anything was overridden. Keep in eye on functionality that did not exist in Drupal 7 and could be lost. That should not stop you from using the migration. If anything, manually apply the configuration that was overwritten by the migration.

Today, we've covered the basics of the Recipes API, how to apply recipes, and used them for creating media types. In our upcoming articles, we’ll walk through migrating user roles, permissions, and text and input formats to your new Drupal 10 site.


Image by Hans from Pixabay