Block Configuration Schema Explainedfor Drupal 11 , and 10

Last updated :  

Last lesson, our configurable block quietly saved a piece of text to configuration and read it back — it just worked, with no extra step visible in the PHP. Behind the scenes, though, Drupal needed one more file to make that safe and predictable: a configuration schema. This short lesson explains what that file does and why Drupal insists on it.

What you'll learn in this lesson

  • Why Drupal needs a formal schema for configuration data, not just "whatever PHP happens to save"
  • How a block's schema key is matched back to its plugin ID
  • What data types like text, string, and boolean mean in a schema, and why the choice matters for translation
  • Where schema files live and how Drupal finds them automatically
Why should you care? If you skip writing a schema for your own module's configuration, two very real things break: drush config:export will complain, and any multilingual site using your module won't be able to translate your block's text. It's a small file that quietly prevents real bugs.

The source file

Path: modules/block_example/config/schema/block_example.schema.yml

block.settings.example_configurable_text:
  type: block_settings
  label: 'Example configurable text block configuration'
  mapping:
    block_example_string:
      type: text
      label: 'Block contents'

How it works

The schema key: block.settings.example_configurable_text

This top-level key follows a strict convention: block.settings.[plugin_id]. The block.settings part is a fixed prefix Drupal's block system always looks for; example_configurable_text must exactly match the id you saw in the @Block annotation back in the previous lesson. Drupal builds this key dynamically at runtime to figure out which schema applies to a given block's saved settings — get the id wrong here, and the schema is silently ignored with no error message at all.

type: block_settings

type: block_settings

block_settings is a base schema type that Drupal core's Block module already defines, covering the properties every block has regardless of plugin — things like label, label_display, provider, status, weight, and region. By declaring this as the parent type, our schema automatically inherits all of that — we only need to describe what's new in the mapping section below, not redefine everything a block already has in common with every other block.

The label key

label: 'Example configurable text block configuration'

This is a human-readable description of the schema as a whole, used inside Drupal's configuration translation UI. It never appears anywhere in the ordinary block administration screens you've been using — it's purely for translators working through /admin/config/regional/config-translation.

The mapping section

mapping:
    block_example_string:
      type: text
      label: 'Block contents'

mapping is where the plugin-specific fields live — and there's exactly one key here, block_example_string, which should look immediately familiar: it's the very same configuration key from defaultConfiguration() and blockSubmit() in the previous lesson. Every value your block actually saves needs an entry here, using the same name, or Drupal has no idea what shape that data is supposed to be.

type: text declares this as a longer, translatable string — as opposed to a handful of other common scalar types:

  • string — a short, non-translatable value (a machine name, an identifier)
  • text — a longer, human-readable, translatable value (what we use here)
  • boolean — true or false
  • integer — a whole number
  • float — a decimal number
  • label — a short translatable string, typically for names

Choosing text here is what makes it possible for a multilingual site to export this block's content for translation at all — pick string by mistake and Drupal will treat the value as non-translatable machine data, even though it's clearly meant to be read by site visitors.

Where schema files live, and how Drupal finds them

Schema files always go in a module's config/schema/ directory, named {module_name}.schema.yml. There's no line in .info.yml or any service definition to register it — Drupal scans and loads every *.schema.yml file from every enabled module automatically.

How this schema gets used at runtime

Four moments where this file actually matters:

  • When the block is placed and saved, Drupal serializes its configuration array using this schema as the reference shape.
  • When configuration is exported (drush config:export), Drupal validates stored values against the schema and writes them to config/sync/block.block.[instance_id].yml.
  • When configuration is imported back in, Drupal validates the incoming values again, rejecting anything of the wrong type.
  • When Drupal scans for translatable strings, it walks this schema tree and flags anything typed text or label as a translation candidate.

See it for yourself

Back on Structure → Block layout, notice the three example blocks — including the configurable one — sitting in their placed regions. Every one of their settings, right down to the exact text you typed in the previous lesson, is validated against a schema like this one behind the scenes, even though nothing about it is visible in the UI.

The Block layout page showing the three Example blocks placed in the Sidebar region alongside the Tools menu block

Quick check: you renamed a block plugin's id in its @Block annotation but forgot to update the schema key to match. What breaks first? Configuration translation and validation for that block silently stop working — Drupal won't find a matching schema anymore, and there's no error to warn you.

Key takeaways

  • The schema key block.settings.[plugin_id] must exactly match the block plugin's @Block annotation id — a mismatch silently breaks validation and translation with no error.
  • Declaring type: block_settings as the parent type inherits all standard block fields from core, so only plugin-specific fields need to be added under mapping.
  • Every key under mapping must correspond to a key the block actually stores via defaultConfiguration() and blockSubmit().
  • Choosing type: text (instead of type: string) is what makes a field translatable and exportable through Drupal's multilingual configuration workflow.
  • Schema files live at config/schema/[module_name].schema.yml and are auto-discovered — nothing else needs to reference them.

Coming up next

That wraps up Blocks. You've now built the full arc: a simple block, a configurable one, and the schema that keeps its configuration validated and translatable. Next up is one of the most-used parts of Drupal development — the Form API. We'll build our first real, standalone form from scratch.