Developer Journal

Intermediate · 10 minute read

Building a Custom Block Plugin

A complete Drupal block plugin using attributes, dependency injection, configuration, and cache metadata.

Last updated August 6, 2026

Block plugins are ideal for reusable, placeable UI backed by application logic. A production block needs more than build(): access, configuration, dependencies, and cacheability all matter.

Use an attributed plugin

Modern Drupal discovers block plugins through the Block attribute. Give the plugin a stable machine ID, translated label, and meaningful category.

Inject the service

Implement ContainerFactoryPluginInterface when the block needs services. Keep storage queries in a repository or domain service rather than embedding them in the plugin.

Return correct cache metadata

If output depends on configuration, entities, routes, roles, or users, declare the corresponding tags and contexts. Correct caching is part of functional correctness.

Working example

#[Block(id: 'journal_featured', admin_label: new TranslatableMarkup('Featured journal article'))]
final class FeaturedArticleBlock extends BlockBase {
  public function build(): array {
    return [
      '#theme' => 'journal_featured',
      '#article' => $this->repository->featured(),
      '#cache' => ['tags' => ['node_list:journal_article']],
    ];
  }
}

Key Takeaways

  • Keep plugins thin and inject application services.
  • Make configuration explicit.
  • Treat cache metadata as required output.

Further Reading