Skip to content

Pattern: Configuration Form (ConfigFormBase)

When to Use

Use ConfigFormBase for admin settings and system configuration. Use FormBase for non-configuration data or temporary workflow data.

Appropriate Use Cases:

  • Admin settings forms
  • System configuration pages
  • Module settings (admin/config paths)
  • Site-wide preferences

When NOT to Use:

  • Non-configuration data storage → Use FormBase + custom storage
  • Entity configuration → Use EntityForm
  • Temporary workflow data → Use FormBase with FormState

Pattern: Implementation

Core Example:

  • File: /web/core/lib/Drupal/Core/Form/ConfigFormBase.php
  • Pattern: Automatic config sync via #config_target (Drupal 10.2+)
  • Study: Lines 106-120 (ConfigTarget), 203-262 (typed validation)

Contrib Example:

  • File: /modules/contrib/ai_provider_anthropic/src/Form/AnthropicConfigForm.php
  • Pattern: Config constants, getEditableConfigNames(), manual save
  • Shows: Traditional pattern (pre-#config_target)

Required Configuration Files:

  1. config/install/mymodule.settings.yml - Default values
  2. config/schema/mymodule.schema.yml - Typed data definition

Pattern: Modern #config_target (Drupal 10.2+)

Declarative Config Binding:

'#config_target' property maps form elements to config properties
Automatic sync: config → form and form → config
Typed validation runs automatically from schema
public function buildForm(array $form, FormStateInterface $form_state) {
  $form['api_key'] = [
    '#type' => 'textfield',
    '#title' => $this->t('API key'),
    '#config_target' => 'mymodule.settings:api_key',
  ];
  return parent::buildForm($form, $form_state);
}

Benefits:

  • Less code (no manual get/set in submit)
  • Automatic validation from schema constraints
  • Works outside forms (recipes, programmatic config)

Reference:

Pattern: Traditional (Pre-10.2)

Manual Config Management:

buildForm(): $config->get('setting')
submitForm(): $config->set('setting', $value)->save()

Example: AnthropicConfigForm lines 78-134

Decision: Which Config Binding

Pattern When to Use Complexity
#config_target Simple config mapping Low
#config_target + ConfigTarget Need transformations Medium
Manual get/set Complex logic, conditional saving High

Override Detection:

ConfigFormBase automatically shows override warnings
Respects config overrides (settings.php)
Use hasOverrides() to check programmatically

Common Mistakes

  • Forgetting getEditableConfigNames() implementation
    • WHY BAD: Config override detection breaks, can't determine which configs form modifies, permission system can't enforce restrictions
  • Not creating schema file (validation won't work)
    • WHY BAD: #config_target validation fails, typed data constraints not enforced, no type checking, config import doesn't validate
  • Using ConfigFormBase for non-config storage
    • WHY BAD: Expects config schema, requires getEditableConfigNames(), config override system confused, unnecessary complexity
  • Hardcoding config names instead of constants
    • WHY BAD: Refactoring breaks all references, typos cause silent failures, IDE can't refactor, no autocomplete

See Also