Creating Block Plugins
When to Use
Building a block that requires programmatic logic, external data, or service integration.
Steps
- Create the block plugin file
- Location:
{module}/src/Plugin/Block/MyBlock.php -
Namespace:
Drupal\{module}\Plugin\Block -
Define the class with #[Block] attribute
#[Block( id: "my_custom_block", admin_label: new TranslatableMarkup("My Custom Block"), category: new TranslatableMarkup("Custom"), )] -
Extend BlockBase and implement build()
class MyBlock extends BlockBase { public function build() { return [ '#markup' => $this->t('Block content'), ]; } } -
Clear cache to discover the plugin
-
drush cror/admin/config/development/performance -
Place the block via UI (
/admin/structure/block) or config
Decision Points
| At this step... | If... | Then... |
|---|---|---|
| Step 2 (attribute) | Block needs services | Add ContainerFactoryPluginInterface, implement create() |
| Step 3 (build) | Content is user-specific | Add cache context user |
| Step 3 (build) | Content changes frequently | Set #cache['max-age'] |
| Step 3 (build) | Block should sometimes hide | Return empty array [] when hidden |
Pattern
Complete block plugin structure:
namespace Drupal\mymodule\Plugin\Block;
use Drupal\Core\Block\Attribute\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\StringTranslation\TranslatableMarkup;
#[Block(
id: "hello_world",
admin_label: new TranslatableMarkup("Hello World"),
category: new TranslatableMarkup("Custom"),
)]
class HelloWorldBlock extends BlockBase {
public function build() {
return [
'#markup' => $this->t('Hello, World!'),
];
}
}
Reference: core/lib/Drupal/Core/Block/Plugin/Block/PageTitleBlock.php, core/modules/system/src/Plugin/Block/SystemMessagesBlock.php
Common Mistakes
- Forgetting
new TranslatableMarkup()in attribute → Causes errors; all text in attributes must be TranslatableMarkup - Returning raw HTML strings → Use render arrays with
#markupor theme functions - Not clearing cache after creating plugin → Drupal won't discover the plugin until cache clear
- Using
echoorprintinbuild()→ Return render arrays only - Hardcoding text without
$this->t()→ Breaks translations
See Also
- Block Configuration Forms (adding settings)
- Dependency Injection in Blocks (injecting services)
- Block Caching Strategies (performance)
- Reference: https://www.drupal.org/docs/creating-modules/creating-custom-blocks/create-a-custom-block-plugin