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 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.
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:
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:
Quick check: if you renamed the configuration key from
block_example_stringto something else indefaultConfiguration()but forgot to update it inbuild(), 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 thatbuild()andblockSubmit()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()inblockSubmit().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;#markupis 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.