A module that quietly assumes another module is already enabled is like a recipe that assumes you already have a working oven — it works fine until someone tries it in a kitchen where that assumption doesn't hold, and then it fails in the most confusing way possible: not at the step that actually needed the oven, but the moment you turn it on and nothing happens. Dependency declarations exist so Drupal can catch that mismatch before your module ever runs, with a clear error instead of a mysterious one.
What you'll learn in this lesson
- How Drupal enforces the
dependencieskey in.info.ymlat install time - The correct
project:module_machine_namesyntax, and why the project prefix matters - How to declare a Composer dependency for a module that needs a third-party PHP library
- The difference between a hard dependency and an optional integration
Declaring module dependencies in .info.yml
name: My Module
type: module
description: Demonstrates proper dependency declaration.
package: Custom
core_version_requirement: ^10.3 || ^11.0
dependencies:
- drupal:node
- drupal:field
- examples:examples
Every entry follows project:module_machine_name — drupal:node means "the node module, which ships as part of Drupal core," while examples:examples means "the examples module, from the separate examples project." Get the project prefix wrong (or omit it) and Drupal can't resolve which package actually provides that module, especially once a site has several contrib projects that happen to ship similarly-named sub-modules.
What actually happens if you get this wrong
Declaring dependencies isn't just documentation — Drupal's module installer genuinely enforces it. Try to enable a module whose declared dependency isn't already enabled, and the install simply refuses, with an explicit message naming the missing module. That's the entire point: without an accurate dependencies list, the exact same failure still happens, just later and less clearly — a fatal Class not found or Call to a member function on null error the first time your code actually tries to use the missing piece, usually in production, usually confusing whoever's debugging it.
Composer dependencies: when you need a real PHP library
If your module needs a third-party PHP library (not another Drupal module), that's a composer.json concern, not an .info.yml one:
{
"name": "drupal/my_module",
"type": "drupal-module",
"require": {
"some-vendor/some-library": "^2.0"
}
}
This tells Composer (not Drupal's module system) to pull in that library when someone runs composer require drupal/my_module. The two mechanisms solve different problems: .info.yml dependencies are checked and enforced by Drupal itself at module-install time; composer.json dependencies are resolved once, when the code is first downloaded, and Drupal has no visibility into them at runtime.
\Drupal::moduleHandler()->moduleExists('media') and adjust behavior accordingly. Declaring an optional integration as a hard dependency forces every installer to enable something they might not actually want.See it for yourself
Look back at any Examples sub-module's .info.yml from this course — plugin_type_example, for instance, depends on both drupal:node and examples:examples. Try (on a test site) removing the examples module's dependency line and re-enabling plugin_type_example without examples already on — Drupal blocks the install outright, which is exactly the mechanism this lesson describes doing its job.
Quick check: your module adds a small enhancement when the Media module happens to be installed, but works completely fine without it. Should you list Media under `dependencies` in your `.info.yml`? No — that would force every site installing your module to also enable Media even if they never wanted it. Check for it at runtime with `moduleHandler()->moduleExists('media')` instead, and treat it as an optional integration.
Key takeaways
- Every entry in `.info.yml`'s `dependencies` list uses `project:module_machine_name` — get the project prefix right, especially for contrib modules with sub-modules of their own.
- Drupal genuinely enforces these at install time — it's the difference between a clear "missing module" error now and a confusing fatal error in production later.
- Third-party PHP libraries are a `composer.json` concern, resolved once at download time — Drupal's module system has no visibility into them at runtime.
- Only declare a hard dependency for something your module genuinely cannot function without; check for optional integrations at runtime with `moduleHandler()->moduleExists()` instead.
Coming up next
That's the whole arc of this Module Development path — from a bare `.info.yml` file all the way through routing, forms, plugins, events, entities, testing, and now packaging a finished module for the world to actually install. Every piece you've built along the way (a controller here, a service there, a config schema, a test suite) is exactly what a real, contributable Drupal module is made of. The next step isn't another lesson — it's picking a real problem on a real site and building the module yourself.