You've built pages, hooked into core events, created blocks, handled forms, queried the database, and defined your own content structures. Now it's time for one of the most important ideas in all of Drupal: the Plugin System. If you've ever wondered how Drupal lets dozens of different modules all provide their own blocks, field types, or image styles — and lets your site pick and choose between them in the admin UI, with zero code changes — plugins are the answer.
Think of it like a wall socket
Here's the mental model that makes plugins click: think of an electrical wall socket. The socket defines a fixed shape — two or three prongs, a certain voltage. It doesn't care whether you plug in a lamp, a phone charger, or a vacuum cleaner. Any device built to that shape just works, and the socket never needs to change to support a new kind of appliance next year.
A Drupal plugin type is the socket. It defines the shape — an interface, really — that says "anything that fits this shape can plug in here." A plugin implementation is the appliance: one specific class that fits that shape and does one specific job. Drupal core defines the Block plugin type once; every module that ships a block — core's "Recent content," your own custom "Uppercase" block from an earlier topic, a contributed module's weather widget — is a separate plugin implementation that fits the same socket. The Block layout admin page doesn't know or care who wrote each block; it just knows they all fit.
Where else you'll meet this exact pattern
This is worth dwelling on, because it's not a niche feature — it's one of the load-bearing architectural ideas of Drupal 8 and later. All of the following are plugin types, built on the exact same underlying machinery you're about to learn:
- Block plugins — every block you can place, from "Recent content" to your own custom ones.
- Field type, widget, and formatter plugins — you'll meet these properly in the Field API topic, but the "Example Color RGB" field type and its widgets are plugins too.
- Field formatter and image effect plugins — how a field's stored value becomes displayed HTML, and how an image style resizes or crops an image.
- Condition plugins — the "Current theme," "Request path," and other rules you see when configuring a block's visibility.
- Migration source/process/destination plugins — the entire Migrate API is plugins all the way down.
- Action plugins — the bulk operations you can run against selected content in an admin listing.
Learn the pattern once, here, using a deliberately silly example (sandwiches), and you'll recognize — and be able to extend — every one of the systems above.
What you'll learn across this topic's three lessons
- This lesson: how the example module that demonstrates plugins is structured — a quick, familiar checkpoint on
.info.ymlbefore the real architecture begins. - Next lesson: the four pieces every new plugin type needs to exist at all — a manager class, an annotation class, an interface, and a base class — read line by line from
SandwichPluginManager.php. - Final lesson: how individual plugin implementations (the actual sandwiches) get discovered and instantiated automatically, with two real, working examples.
/**
* @Sandwich(
* id = "meatball_sandwich",
* label = @Translation("Meatball sandwich"),
* calories = 1200
* )
*/
class MeatballSandwich extends SandwichBase { ... }
That @Sandwich(...) block is how a manager class discovers this plugin automatically, without you ever registering it in a list anywhere. Keep that image in mind — it's the payoff this whole topic is building toward.The source file
Path (relative to the Examples module's root): modules/plugin_type_example/plugin_type_example.info.yml
name: Plugin Type Example
type: module
description: Provides an example of defining a plugin type.
package: Example modules
core_version_requirement: ^10.3 || ^11.0
dependencies:
- drupal:node
- examples:examples
# Information added by Drupal.org packaging script on 2024-09-09
version: '4.0.4'
project: 'examples'
datestamp: 1725887893
How it works, key by key
name, type, and description
The human-readable name shown under Extend is Plugin Type Example — clearly communicating the module's educational purpose. type: module tells Drupal's extension system to treat this as a module rather than a theme or install profile. description gives administrators a precise one-line summary: "Provides an example of defining a plugin type."
package
package: Example modules
Groups this module under the same "Example modules" heading as every other module you've enabled throughout this course, keeping them together in the admin UI rather than scattered under a generic "Other" heading.
core_version_requirement
core_version_requirement: ^10.3 || ^11.0
The familiar Composer constraint syntax: compatible with Drupal 10.3 and up, through the entire 11.x series. This key replaced the older core: 8.x approach and is mandatory for any module you want to install on a modern Drupal site — get it wrong, and the module simply won't appear as installable, with no further explanation from the UI.
dependencies
dependencies:
- drupal:node
- examples:examples
Two dependencies, in the familiar project:module_machine_name format:
drupal:node— the core Node module. The plugin type example uses nodes to demonstrate how a plugin can interact with real content entities.examples:examples— the shared base module every Examples sub-module depends on, providing common utilities and routes used across the whole Examples project.
Declaring dependencies here means Drupal refuses to enable plugin_type_example unless both are already active — one less category of runtime error you have to guard against yourself.
The packaging keys
version: '4.0.4'
project: 'examples'
datestamp: 1725887893
As always in this course: injected automatically by Drupal.org's packaging script the moment a release is built, never written by hand, and absent from any module you develop locally until you publish an official release.
Here's the important thing to notice: nothing in this file mentions plugins at all. The entire plugin architecture — the manager, the annotation, the interface, the discoverable plugin classes — lives entirely in PHP, in files you haven't opened yet. The
.info.ymlfile's only job is still the one it's always had: tell Drupal this folder is a module, and what it needs to run. Everything plugin-specific starts in the next lesson.
See it for yourself
Visit /examples/plugin-type-example on your own DDEV site. You're looking at the "Sandwich plugin definitions" and "Sandwich plugins" lists — the concrete, rendered output of the entire plugin system you're about to learn to build from scratch.
Every line on that page — the sandwich names, their calorie counts, their descriptions — was discovered, not hardcoded into a template. Somewhere, a manager class scanned every enabled module looking for classes annotated @Sandwich(...), built a list of what it found, and this page just looped over that list. That discovery mechanism is exactly what the next lesson opens up.
Key takeaways
- A plugin type module's
.info.ymlfile looks exactly like any other module's —name,type,description,package,core_version_requirement, anddependenciesare unchanged. - The plugin architecture itself is implemented entirely in PHP (a manager class, an annotation class, an interface, and a base class) — the info file has no special plugin-related keys at all.
- Blocks, field types, field widgets, field formatters, image effects, migrate plugins, and action plugins are all built on this exact same underlying architecture — learning it here transfers directly to every one of them.
- The "wall socket" mental model: a plugin type defines a fixed shape (an interface); any plugin implementation built to that shape can be discovered and used interchangeably, without the discovering code ever needing to know it exists in advance.
core_version_requirementusing Composer syntax remains mandatory for Drupal 10/11 compatibility, regardless of what kind of module you're writing.
Coming up next
Time to open the real plugin architecture. In the next lesson we'll read SandwichPluginManager.php line by line and see exactly what it takes to define a brand-new plugin type from scratch: a manager, an interface, an annotation class, and a base class, all working together to make that @Sandwich(...) annotation from the preview above actually mean something.