Drupal 9: Anatomy Of The Drupal Recommended Composer File

According to the official Drupal documentation, to create a new site using composer you should use a composer template project called drupal/recommended-project. This has a default composer.json file setup with some values that will help you get up and running swiftly with a new Drupal project.

It's a good initiative to get you up and running with a standard Drupal site pretty quickly. I've used this composer project a number of times now, but I haven't really looked at what's in it. I thought I would dive in and see exactly that's in there and dissect it line by line.

To reiterate what's in the Drupal documentation, to create a brand new Drupal project using composer use the following command.

composer create-project drupal/recommended-project my-project

This creates the following directory structure in a directory called my-project that contains all of your Drupal dependencies installed and ready to go.

$ ls -1

The composer.json file included in this project is the same from the original recommended project as it is used to construct the project.

Let's start off at the top.

Composer Metadata

The first few lines of the project are a bunch of meta data items containing information about the project and where to get support.

    "name": "drupal/recommended-project",
    "description": "Project template for Drupal 9 projects with a relocated document root",
    "type": "project",
    "license": "GPL-2.0-or-later",
    "homepage": "https://www.drupal.org/project/drupal",
    "support": {
        "docs": "https://www.drupal.org/docs/user_guide/en/index.html",
        "chat": "https://www.drupal.org/node/314178"

These are important for Drupal, but are not needed for a Drupal project website that you are building yourself. In fact, you can reduce this down to name, description and licence and change these as you require. The only requirement is that the name of the project must be in the format vendor name, a forward slash, and a package name.


The next item is called "repositories". This section registers the packages.drupal.org/8 site as an endpoint that is used by composer to find Drupal packages.

    "repositories": [
            "type": "composer",
            "url": "https://packages.drupal.org/8"

This essentially means that we can now require Drupal packages by running commands like this.

composer require drupal/token

Which will know to look at packages.drupal.org/8 in order to find out more about the package requested.


Now we get onto the project dependencies, which are any other projects are included into the recommended Drupal project. By default, it comes with four dependencies.

    "require": {
        "composer/installers": "^1.9",
        "drupal/core-composer-scaffold": "^9",
        "drupal/core-project-message": "^9",
        "drupal/core-recommended": "^9"

Looking into each of these in detail:

  • composer/installers - This package allows package authors to specify different types of projects so that they can be treated in different ways. What this means is that you can create a project of with the type of drupal-module and it can be treated like a Drupal module. There are many other project types available and the different types of Drupal project are just a part of this package. Note that you will still need to include a location that the different projects will go within your project, but that is handled later in the file, within the extra section.
  • drupal/core-composer-scaffold - A package that provides a mechanism for moving the Drupal files (index.php, update.php, robots.txt etc) into the correct places. Like the composer/installers package, this package also has a reference in the extra section (which we'll go into later).
  • drupal/core-project-message - This is a composer plugin that displays a message after Composer installation processes has finished. This is used to welcome you to Drupal and show some initial hints and tips on where to get help. Once the site has been installed you can remove this package as it has done its job.
  • drupal/core-recommended - This package does not have any code of it's own but is used to include Drupal and a bunch of other packages used by Drupal core. Including the main drupal/core package there is also twig for theme work and a few symfony components that Drupal uses.

Note that I'm deliberately looking at the 9.1.x branch in the Drupal projects above.

As a point of interest, due to the above projects including other projects the number of dependencies in a newly installed Drupal project is 58. To see the list of included projects just run composer show.


The require-dev section is used to install packages that are used when running tests or other development related tasks.

    "require-dev": {
        "drupal/core-dev": "^9"

This includes drupal/code-dev package, which in turn includes packages like Behat, PHPUnit and Prophecy that are used to run Drupal unit tests. The idea behind these being here is that they don't need to be installed on a production server and so can be separated out.


Conflict is used to specify certain projects that conflict with this project. This means that any packages added to this section will cause a conflict if an attempt is made to add them as a requirement.

    "conflict": {
        "drupal/drupal": "*"

In this case a conflict is being set against the drupal/drupal package, which is basically a copy of the Drupal codebase. It makes very little sense to add both drupal/core and drupal/drupal to the same project so a conflict is made to prevent this.

Package settings

Next is two settings that are used to set what kind of projects you are able to include into this project.

    "minimum-stability": "dev",
    "prefer-stable": true,

Setting the minimum-stability to dev means that we can include development packages (ie, packages without any tagged release) into the project. The default value for this setting in composer is stable, which will cause composer to only include full releases into the project. The Drupal contributed code landscape does contain some non-stable packages and so it makes sense to include this.

The prefer-stable option tells composer that if you include any package that it should pick the "latest" release of that package (within the constraints of any version numbers specified). The idea is that if you include a package and there is a dev and an alpha version available, composer will select the alpha version and include that.


The config section is used to apply any miscellaneous settings to composer.

    "config": {
        "sort-packages": true

In this case we are setting the option to sort the added packages, which means that any packages added to this composer file will be added in an alphabetical manner.


The final and longest section is extra. The values in this section can be accessed by scripts run by composer so the basic idea is that any scripts run within composer will pick up this data and be able to do things with it. As it's quite a long section I have split this up into chunks.

The first part of the extra section is drupal-scaffold, which is used by the drupal-scaffold package and will inform the package the location of the web root directory. When a composer install command is run the drupal-scaffold package will run, pick up this information and run it's scaffolding actions against the provided web-root directory.

    "extra": {
        "drupal-scaffold": {
            "locations": {
                "web-root": "web/"

Next is the installer-paths section, which details the final location of the different types of Drupal package. This is used by the composer/installers package to perform the move operations on those packages

        "installer-paths": {
            "web/core": ["type:drupal-core"],
            "web/libraries/{$name}": ["type:drupal-library"],
            "web/modules/contrib/{$name}": ["type:drupal-module"],
            "web/profiles/contrib/{$name}": ["type:drupal-profile"],
            "web/themes/contrib/{$name}": ["type:drupal-theme"],
            "drush/Commands/contrib/{$name}": ["type:drupal-drush"],
            "web/modules/custom/{$name}": ["type:drupal-custom-module"],
            "web/profiles/custom/{$name}": ["type:drupal-custom-profile"],
            "web/themes/custom/{$name}": ["type:drupal-custom-theme"]

As an example from the above list, take the following line:

web/modules/contrib/{$name}": ["type:drupal-module"],

This sets the destination of contributed modules, so when a composer package of the type "drupal-module" is installed it will automatically get moved to the location web/modules/contrib/. As you can see from the list above there are a number of different packages defined against Drupal and even one configuring Drush commands Drush.

The final section is drupal-core-project-message and is used by the drupal/core-project-message package to print out a message when the package is first created. You can quite happily remove this section once the project is installed as it is only meant to be used once.

        "drupal-core-project-message": {
            "include-keys": ["homepage", "support"],
            "post-create-project-cmd-message": [
                "<bg=blue;fg=white>                                                         </>",
                "<bg=blue;fg=white>  Congratulations, you’ve installed the Drupal codebase  </>",
                "<bg=blue;fg=white>  from the drupal/recommended-project template!          </>",
                "<bg=blue;fg=white>                                                         </>",
                "<bg=yellow;fg=black>Next steps</>:",

                "  * Install the site: https://www.drupal.org/docs/8/install",
                "  * Read the user guide: https://www.drupal.org/docs/user_guide/en/index.html",
                "  * Get support: https://www.drupal.org/support",
                "  * Get involved with the Drupal community:",
                "      https://www.drupal.org/getting-involved",
                "  * Remove the plugin that prints this message:",
                "      composer remove drupal/core-project-message"

All this means that when you run the command to create a project you will see a message. Once all of the composer dependencies have run and the rest of the post install actions has finished, the post-coreate-project-cmd-message section is run. This prints out the following.

Drupal composer scaffold command line output.

This is the end of the composer.json file. I've learned a few things about composer and how the Drupal recommended project has been setup to install everything with a simple composer.json file. I hope it's been helpful for you as well.

Final Steps

I have also stated a couple of times above that you can remove certain parts of the composer.json file once the project setup is complete. Basically, once the project is setup you can run the following command to correctly remove the drupal/core-project-message package from your codebase.

composer remove drupal/core-project-message

You can then remove the drupal-core-project-message part from the extra section in the composer.json file.

It's a good idea to also tweak the authorship meta data section at the top of the composer.json file as well. As I mentioned before you only really need to have the name, description and license in this section. Something like this is absolutely fine.

    "name": "example/test",
    "description": "My custom project.",
    "license": "GPL-2.0-or-later",

Once you have edited your composer.json file you must then run composer update --lock to also make those changes in your composer.lock file. This will ensure that both files and in sync. If you want to ensure that everything is correct just run composer validate, which will show you any problems you might have with the file.

Add new comment

The content of this field is kept private and will not be shown publicly.
1 + 4 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.