Ever hit a wall when developing a website in Drupal and thought, "If only I could have the particular functionality for my website"? This is exactly where the magic of Drupal module development comes in.
Drupal offers powerful out-of-the-box features, but its real strength lies in how easily it can be extended. With modules, you're not limited to what's provided by core or contributed projects; you can easily shape Drupal to match your business needs, workflows, and customer experiences.
At its core, Drupal modules are bundles of code (PHP, YAML, and sometimes Twig) that plug directly into Drupal to expand its capabilities. They are the building blocks of any sophisticated Drupal site, giving you the freedom to create features that no pre-built module can deliver.
By the end of this guide, you'll not only understand how modules work, but also build a simple, functional custom module in Drupal 10.
The Blueprint: Understanding the Anatomy of a Module
Whether you are writing the code yourself or hiring Drupal development services to extend the functionality of your website, it’s important to understand the structure that every Drupal module follows. Drupal recognizes a module through a set of required files and directories. These files define the module, tell Drupal how to use it, and provide the logic that powers its functionality.
Where Modules Live
All custom modules should be placed in the /modules/custom directory of your Drupal project. Inside this folder, create a new directory named after your module’s machine name. The machine name should use only lowercase letters and underscores, such as hello_world. This setup is an essential step in mastering Drupal module development.
The Info File: my_module.info.yml
The .info.yml file is required for every module. It tells Drupal that your module exists and provides basic metadata. Understanding this file is critical when learning how to create a Drupal module.
The Routing File: my_module.routing.yml
This file defines the routes (URLs) that your module provides. Each route is mapped to a controller or callback. For example:
module_name.content:
path: '/module-name'
defaults:
_controller: '\Drupal\module_name\Controller\ModuleNameController::content'
_title: 'Module Name'
requirements:
_permission: 'access content'
Here’s what’s happening:
- path: Defines the URL.
- _controller: Points to the PHP class and method that will handle the request.
- _title: Provides the page title.
- requirements: Defines permissions needed to access the route.
This routing setup is often the first introduction to a Drupal custom page controller.
The Source Directory: src/
Your module’s custom PHP code goes inside a src/ directory. This directory follows PSR-4 standards for organizing code. Common subdirectories include:
- Controller/ – Classes that return responses for routes.
- Plugin/ – Classes for blocks, field types, and other plugin types.
- Form/ – Classes for configuration or custom forms.
Drupal automatically discovers these classes based on their namespace and location in the src/ folder. This structure reflects the Drupal module folder structure that all developers should understand.
Steps to Building a Drupal Module
Creating a custom module in Drupal 10 involves a series of well-defined steps. Each step introduces a specific file or concept that tells Drupal how to recognize, configure, and run your module. By following these steps in order, you can build modules that are consistent, maintainable, and extend Drupal in a reliable way.
Step 1: Naming and Placing Your Drupal Module
The first step in creating a Drupal module is deciding on its name and placing it in the correct directory. Drupal has strict conventions for both, and following them ensures that your module is recognized and works correctly.
Placement
All custom modules should be stored in the /modules/custom directory within your Drupal project. If the custom folder does not already exist, you can create it manually. Placing your module here keeps it separate from core and contributed modules, which live in different directories. This separation makes upgrades and maintenance easier, especially when learning through a Drupal 10 tutorial for developers.
Naming Conventions
Each module must have a unique machine name, which also becomes the name of the module’s folder. The machine name should:
- Use only lowercase letters and underscores.
- Avoid spaces or special characters.
- Be descriptive but concise.
Examples of correct module names following the conventions
- my_custom_module
- hello_world
The machine name is used throughout Drupal as a unique identifier for your module, so choosing a clear and consistent name at the start is important.
Step 2: Let Drupal Know About Your Module with an .info.yml File
Drupal cannot detect a module unless it has an .info.yml file. This file acts as the module’s registration form, telling Drupal its name, purpose, and compatibility details. Without it, the module simply won’t appear in the administration interface.
File Location
Inside your module’s folder (for example, /modules/custom/my_custom_module/), create a file named:
my_custom_module.info.yml
The file name must match your module’s machine name.
Basic Structure
Here’s a minimal example of what the .info.yml file should contain:
name: Module Name
type: module
description: Provides custom functionality for the site.
core_version_requirement: ^10
package: Custom
Explanation of Keys
- name: The human-readable name that will be shown in the Drupal admin UI.
- type: Always set to module.
- description: A short explanation of what the module does.
- core_version_requirement: Specifies which version(s) of Drupal core the module supports (e.g., ^10).
- package: Groups your module under a category in the module administration page, making it easier to find.
Optional Keys
You can also include additional metadata, such as:
dependencies:
- drupal:node
version: 1.0
- dependencies: Lists other modules that must be enabled for yours to work.
- version: A version number for your module (useful for maintenance and updates).
Additionally, the description and package key mentioned in the basic structure are also considered optional. Once the .info.yml file is in place, clear the Drupal cache or refresh the Extend page in the admin interface. Your module will now appear in the list of available modules, ready to be enabled. This step is a classic Drupal configuration schema example, showing how metadata informs Drupal about your module.
Step 3: Include Default Configuration in your Drupal Module
Drupal’s configuration management system allows you to package configuration settings with your module so that they are automatically available when the module is installed. This is useful if your module needs to provide default settings, blocks, views, or other configurations without requiring manual setup after installation.
Where to Place Configuration Files
Inside your module’s folder, create a directory called:
config/install
Any YAML files placed here will be imported into the site’s active configuration when the module is first enabled.
Examples of Default Configuration Files
Block Placement
# config/install/block.block.my_custom_block.yml
id: my_custom_block
theme: bartik
status: true
region: sidebar_first
plugin: my_custom_block
settings:
id: my_custom_block
label: 'My Custom Block'
provider: my_custom_module
This example places a custom block (created by your module) in the sidebar of the Bartik theme as soon as the module is enabled. Developers who want to explore more about creating blocks should check a Drupal block plugin tutorial for deeper insights.
View Definition
# config/install/views.view.my_custom_view.yml
id: my_custom_view
label: 'My Custom View'
module: views
description: 'A default view provided by my_custom_module.'
This creates a new view automatically, instead of requiring a site builder to create it manually.
Benefits of Including Default Configuration
- Ensures a consistent setup every time the module is installed.
- Saves administrators time by removing the need for manual configuration.
- Makes modules portable—configurations travel with the codebase.
- Supports best practices for deployment, since configuration is stored in version control.
In short, if your module depends on certain settings or components, packaging them as default configurations ensures the module works out of the box.
Step 4: Defining and Using Your Own Configuration in Your Drupal Module
Sometimes your module needs its own settings, not just defaults for other components. Drupal lets you define custom configuration so your module can store and use its own values.
Creating a Configuration File
Add a YAML file inside your module’s config/install folder, using your module’s machine name as a prefix. For example:
# config/install/mymodule.settings.yml
message: 'Hello'
langcode: 'en'
This defines a simple configuration with a message string and a language code.
Making It Available in Code
You can load these values anywhere in your module:
$config = \Drupal::config('mymodule.settings');
print $config->get('message'); // Outputs: Hello
And you can update them when needed:
$config = \Drupal::service('config.factory')->getEditable('mymodule.settings');
$config->set('message', 'Hi')->save();
Why Use Custom Configuration?
- Lets your module have its own settings.
- Ensures values can be stored, deployed, and version-controlled.
- Works with Drupal’s multilingual system if translation is enabled.
This is a great example of how the Drupal plugin system ties into configuration, allowing developers to build flexible and reusable features.
Step 5: Create a Custom Page Using a Controller
To add a new page in Drupal, you define a route and connect it to a controller. The controller is a PHP class that returns what should be displayed on that page.
Define the Route
Create a file called mymodule.routing.yml in your module folder:
mymodule.content:
path: '/my-page'
defaults:
_controller: '\Drupal\mymodule\Controller\MyController::content'
_title: 'My Custom Page'
requirements:
_permission: 'access content'
This maps the URL /my-page to a controller method.
Create the Controller
Inside src/Controller/, add MyController.php:
<?php
namespace Drupal\mymodule\Controller;
use Drupal\Core\Controller\ControllerBase;
class MyController extends ControllerBase {
public function content() {
return [
'#markup' => 'Hello from my custom page!',
];
}
}
This tells Drupal to render a simple message when someone visits /my-page.
Key Points
- Routes go in mymodule.routing.yml.
- Controllers live in src/Controller/.
- Controllers return render arrays (like #markup), which Drupal uses for output.
This example is a straightforward Drupal custom page controller, giving developers a foundation for building more complex functionality.
Step 6: Creating Custom Blocks
Blocks are reusable pieces of content that you can place in regions of your theme. In Drupal, custom blocks are created using the Plugin system.
Create a Block Class
Inside your module, add a file at:
src/Plugin/Block/MyCustomBlock.php
Example:
<?php
namespace Drupal\mymodule\Plugin\Block;
use Drupal\Core\Block\BlockBase;
/**
* Provides a 'My Custom Block' block.
*/
#[Block(
id: "my_custom_block",
admin_label: new Translation("My Custom Block")
)]
class MyCustomBlock extends BlockBase {
public function build() {
return [
'#markup' => 'This is my custom block content.',
];
}
}
Place the Block code above in your module.
- Enable your module.
- Go to Structure → Block Layout in the admin UI.
- You’ll see My Custom Block available to place in any region.
Key Points
- Blocks are defined as plugins with PHP 8 attributes since Drupal 9.4+.
- The build() method defines what the block displays.
- They can include markup, render arrays, or references to templates.
This is a practical example of working with a Drupal custom field type since blocks often interact with fields and plugins to extend site functionality.
Conclusion
You’ve now walked through the essential steps of Drupal module development, starting with naming and placing your module, registering it with an .info.yml file, adding configuration, defining routes and controllers for pages, and creating reusable custom blocks. Whether you are creating a new Drupal website, or migrating your website to Drupal 10 and want to extend web functionality, these steps form the foundation of extending Drupal beyond its core and contributed modules.
With this knowledge, you can start tailoring Drupal to match your organization’s unique requirements. From here, the possibilities grow: advanced field types, custom plugins, service integration, and more. Each new step builds on the fundamentals you’ve already practiced, and for deeper insights, you can always revisit a Drupal 10 tutorial for developers as you expand your skills.
If you’re ready to take your Drupal site further or need expert support for custom development, our team is here to help. Contact us today to discuss how we can bring your Drupal vision to life.
FAQ
Frequently Asked Questions
To create a custom module in Drupal 10, you:
- Create a new folder for your module inside the modules/custom directory.
- Add a module information file (.info.yml) to define the module’s name, description, and compatibility.
- Optionally add a .module file if you need to implement hooks.
- Add routing or menu link files if your module provides pages or menu items.
- Clear Drupal’s cache so the system recognizes your new module.
- Enable the module from the Extend page or using Drush.
You can add configuration to a Drupal module in several ways:
- By providing default configuration files inside a config/install folder.
- By defining configuration schemas inside a config/schema folder.
- By creating administrative settings forms where site administrators can change values.
- By using Drupal’s configuration API in your code to read or update settings.
A typical custom module includes:
- An .info.yml file (required).
- An optional .module file if hooks are needed.
- Optional YAML files for routing, permissions, and menu links.
- A src folder for PHP classes such as controllers, plugins, or forms.
- A templates folder for Twig template overrides.
- A config folder for configuration and schema files.
Didn’t find what you were looking for here?