Extra Block Types (EBT) - New Layout Builder experience❗

Extra Block Types (EBT) - styled, customizable block types: Slideshows, Tabs, Cards, Accordions and many others. Built-in settings for background, DOM Box, javascript plugins. Experience the future of layout building today.

Demo EBT modules Download EBT modules

❗Extra Paragraph Types (EPT) - New Paragraphs experience

Extra Paragraph Types (EPT) - analogical paragraph based set of modules.

Demo EPT modules Download EPT modules

Scroll
13/04/2025, by Ivan

Overview

Blocks in Drupal 8 actually consist of two separate API structures to create a user interface similar to previous iterations of Drupal. These two APIs are the Block Plugin API, which is a reusable standalone API, and the Block Entity API, which is a Drupal 8-specific implementation for placing blocks and controlling their visibility.

Creating Blocks with the Block Plugin API

To define blocks in your module's code, you’ll need to learn and understand the Plugin API and, in particular, the concept of annotation-based plugin discovery, which is the mechanism Drupal 8 uses to find your block definition code.

Creating a custom block defined in your module includes the following steps:

Making Your Block Visible to Drupal and Users

Drupal uses PSR-4 standard for discovery. Assuming your module is named "fax", the code for your custom block(s) should be placed in fax/src/Plugin/Block/. Each file in that directory must be named according to the class it contains. If we are defining a class called FaxBlock, it should be in a file named fax/src/Plugin/Block/FaxBlock.php and contain the following example content:

namespace Drupal\fax\Plugin\Block;

use Drupal\Core\Block\BlockBase;

/**
 * Provides a 'Fax' block.
 *
 * @Block(
 *   id = "fax_block",
 *   admin_label = @Translation("Fax block"),
 * )
 */
class FaxBlock extends BlockBase {
  // Override BlockPluginInterface methods here.
}

The id in the annotation defines the unique machine-readable ID of your block, and the admin_label defines a human-readable name shown in the admin UI. Available annotation properties are defined in \Drupal\Core\Block\Annotation\Block.

The two most common methods to override are:

  • BlockPluginInterface::build() – returns a render array for the block’s content.
  • BlockBase::access() – controls block visibility and should return an AccessResult object.

Adding Custom Configuration Options to Your Block

You can add custom configuration options to the block config form by overriding blockForm() and blockSubmit(), using setConfigurationValue() and getConfiguration().

Example of a block with a configurable fax number:

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * Provides a 'Fax' block.
 *
 * @Block(
 *   id = "fax_block",
 *   admin_label = @Translation("Fax block"),
 * )
 */
class FaxBlock extends BlockBase {

  public function build() {
    $config = $this->getConfiguration();
    $fax_number = isset($config['fax_number']) ? $config['fax_number'] : '';
    return [
      '#markup' => $this->t('The fax number is @number!', ['@number' => $fax_number]),
    ];
  }

  public function blockForm($form, FormStateInterface $form_state) {
    $form = parent::blockForm($form, $form_state);
    $config = $this->getConfiguration();
    $form['fax_number'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Fax number'),
      '#default_value' => $config['fax_number'] ?? '',
    ];
    return $form;
  }

  public function blockSubmit($form, FormStateInterface $form_state) {
    $this->setConfigurationValue('fax_number', $form_state->getValue('fax_number'));
  }

  public function blockValidate($form, FormStateInterface $form_state) {
    $fax_number = $form_state->getValue('fax_number');
    if (!is_numeric($fax_number)) {
      $form_state->setErrorByName('fax_number', $this->t('Needs to be an integer'));
    }
  }
}

You can also use getConfiguration() in build() to display config data to users. The access() method can contain complex logic to control visibility.

Example of an access control method:

public function access(AccountInterface $account, $return_as_object = FALSE) {
  return \Drupal\Core\Access\AccessResult::allowedIf($account->isAuthenticated());
}

Example with cache tags (learn more about cache tags):

public function getCacheTags() {
  return \Drupal\Core\Cache\Cache::mergeTags(parent::getCacheTags(), ['node_list']);
}

Disable caching by setting max-age to 0:

public function getCacheMaxAge() {
  return 0;
}

Drupal’s online documentation is © 2000-2020 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.