Configurable Block Plugins: Adding a Settings Formfor Drupal 11 , and 10

Last updated :  

The two blocks in the last lesson always show the exact same thing. That's fine for a demo, but real-world blocks almost always need to be configurable — an administrator should be able to place the block and then customize what it says without touching a line of code. This lesson shows you exactly how that works, using a block that lets you type in your own custom text.

What you'll learn in this lesson

  • How defaultConfiguration() gives a freshly-placed block its starting values
  • How blockForm() adds custom fields to the block's configuration form
  • How blockSubmit() saves what an administrator typed back into persistent configuration
  • How build() reads that saved configuration back out when rendering the block
The big idea: these four methods form a complete loop — default value in, admin edits it, submit saves it, build displays it. Once this pattern clicks, you'll recognize it in almost every configurable Drupal plugin you ever write, not just blocks.

The source file

Path: modules/block_example/src/Plugin/Block/ExampleConfigurableTextBlock.php

<?php

namespace Drupal\block_example\Plugin\Block;

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

/**
 * Provides a 'Example: configurable text string' block.
 *
 * Drupal\Core\Block\BlockBase gives us a very useful set of basic functionality
 * for this configurable block. We can just fill in a few of the blanks with
 * defaultConfiguration(), blockForm(), blockSubmit(), and build().
 *
 * @Block(
 *   id = "example_configurable_text",
 *   admin_label = @Translation("Example: configurable text")
 * )
 */
class ExampleConfigurableTextBlock extends BlockBase {

  /**
   * {@inheritdoc}
   *
   * This method sets the block default configuration. This configuration
   * determines the block's behavior when a block is initially placed in a
   * region. Default values for the block configuration form should be added to
   * the configuration array. System default configurations are assembled in
   * BlockBase::__construct() e.g. cache setting and block title visibility.
   *
   * @see \Drupal\block\BlockBase::__construct()
   */
  public function defaultConfiguration() {
    return [
      'block_example_string' => $this->t('A default value. This block was created at %time', ['%time' => date('c')]),
    ];
  }

  /**
   * {@inheritdoc}
   *
   * This method defines form elements for custom block configuration. Standard
   * block configuration fields are added by BlockBase::buildConfigurationForm()
   * (block title and title visibility) and BlockFormController::form() (block
   * visibility settings).
   *
   * @see \Drupal\block\BlockBase::buildConfigurationForm()
   * @see \Drupal\block\BlockFormController::form()
   */
  public function blockForm($form, FormStateInterface $form_state) {
    $form['block_example_string_text'] = [
      '#type' => 'textarea',
      '#title' => $this->t('Block contents'),
      '#description' => $this->t('This text will appear in the example block.'),
      '#default_value' => $this->configuration['block_example_string'],
    ];
    return $form;
  }

  /**
   * {@inheritdoc}
   *
   * This method processes the blockForm() form fields when the block
   * configuration form is submitted.
   *
   * The blockValidate() method can be used to validate the form submission.
   */
  public function blockSubmit($form, FormStateInterface $form_state) {
    $this->configuration['block_example_string']
      = $form_state->getValue('block_example_string_text');
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
    return [
      '#markup' => $this->configuration['block_example_string'],
    ];
  }

}

How it works

defaultConfiguration() — the starting value

public function defaultConfiguration() {
  return [
    'block_example_string' => $this->t('A default value. This block was created at %time', ['%time' => date('c')]),
  ];
}

This method returns the configuration values a fresh instance of this block starts with, before an administrator has changed anything. Drupal merges these with system-level defaults (like block title visibility) that BlockBase::__construct() already handles for you.

The key detail to notice: 'block_example_string' is a name we chose, and it's going to reappear in every other method below, unchanged. That consistency is not optional — it's how these four methods stay in sync with each other. The %time placeholder is filled in with date('c') (an ISO 8601 timestamp), so every freshly-placed block instance gets a distinct default value, handy for telling multiple placements apart.

blockForm() — building the settings form

public function blockForm($form, FormStateInterface $form_state) {
  $form['block_example_string_text'] = [
    '#type' => 'textarea',
    '#title' => $this->t('Block contents'),
    '#description' => $this->t('This text will appear in the example block.'),
    '#default_value' => $this->configuration['block_example_string'],
  ];
  return $form;
}

Drupal calls blockForm() automatically while building the block's configuration form — the same form you reach by clicking "Configure" on a placed block. The $form array you receive already contains the standard fields BlockBase adds (title, title visibility); this method adds one more field to it and returns the result.

Worth noticing: the form field is named block_example_string_text, but the configuration key is block_example_string — deliberately different names. The form field name is only used to read the submitted value; the configuration key is where it's permanently stored. They're linked only by the explicit code in blockSubmit() below.

'#default_value' => $this->configuration['block_example_string'] pre-fills the textarea with whatever is currently saved — either the value from defaultConfiguration() on a brand-new placement, or the administrator's own previously-saved text on every subsequent edit.

blockSubmit() — saving what was typed

public function blockSubmit($form, FormStateInterface $form_state) {
  $this->configuration['block_example_string']
    = $form_state->getValue('block_example_string_text');
}

Once the configuration form passes validation and is submitted, Drupal calls blockSubmit(). Its entire job is to pull the submitted value out of $form_state using the form field's name, and write it into $this->configuration using the configuration key. Drupal takes care of persisting $this->configuration after this — you never call a "save" method yourself.

build() — reading it back out

public function build() {
  return [
    '#markup' => $this->configuration['block_example_string'],
  ];
}

This is called every time Drupal needs to render the block on an actual page — and now you can see the whole loop close: it reads the exact same block_example_string key that defaultConfiguration() initialized and blockSubmit() may have overwritten, and outputs it as markup.

See it for yourself

Place "Example: configurable text" from Structure → Block layout, and before saving, type your own message into the "Block contents" field:

The configurable block's settings form with custom block contents text typed into the textarea

Save the block, then visit a page where it's placed. Your custom text appears exactly as typed — no code changes, no cache clear needed beyond the normal save:

The configurable text block placed and rendered live on the page with the custom saved text visible

Quick check: if you renamed the configuration key from block_example_string to something else in defaultConfiguration() but forgot to update it in build(), what would happen? The block would render nothing useful — build() would be reading a configuration key that was never actually set, since the three methods only stay in sync because the string matches everywhere by hand.

Key takeaways

  • defaultConfiguration() defines the starting stored values for a block instance, using the same configuration keys that build() and blockSubmit() reference — all three must agree.
  • blockForm() injects custom fields into the existing configuration form; the field's name here is the retrieval key for $form_state->getValue() in blockSubmit().
  • blockSubmit() is the only correct place to write submitted values into $this->configuration — Drupal persists it automatically afterward.
  • The form field name and the configuration storage key can differ, and often do — they play different roles and are connected only through your own code.
  • build() returns a render array; #markup is the simplest way to output stored text, and Drupal applies XSS filtering to it automatically.

Coming up next

We've seen the block save a plain PHP array of configuration — but how does Drupal know that block_example_string should be a translatable, exportable piece of configuration rather than an opaque blob? That's the job of a configuration schema, and it's exactly what the next lesson covers.