Using #default_value for form elementsfor Drupal 11 , 10 , 9 , and 8

Last updated :  

You met #default_value's basic behavior back in Form Basics — first-render-only, distinct from #value. This lesson goes deeper: where a real default value actually comes from in practice, since it's rarely a hardcoded literal the way earlier examples showed.

What you'll learn in this lesson

  • Pulling defaults from configuration, not hardcoding them
  • Pulling defaults from an existing entity, for an edit form
  • Conditional defaults based on the current user

Defaults from configuration

public function buildForm(array $form, FormStateInterface $form_state) {
  $config = $this->config('mymodule.settings');
  $form['items_per_page'] = [
    '#type' => 'number',
    '#title' => $this->t('Items per page'),
    '#default_value' => $config->get('items_per_page') ?? 10,
  ];
  return $form;
}

This is the real pattern behind nearly every settings form on a Drupal site (covered fully in the Configuration Forms section later) — read the current saved value, fall back to a sensible literal if it's never been set, and let the user see and change what's actually configured right now, not a fresh blank field every time.

Defaults from an existing entity — the edit-form pattern

public function buildForm(array $form, FormStateInterface $form_state, NodeInterface $node = NULL) {
  $form['title'] = [
    '#type' => 'textfield',
    '#title' => $this->t('Title'),
    '#default_value' => $node ? $node->getTitle() : '',
  ];
  return $form;
}
The same form class serving both add and edit. Making the entity argument optional (NodeInterface $node = NULL) lets one form class handle both "create new" (no node passed, defaults empty) and "edit existing" (node passed, defaults pre-filled from it) — a genuinely common pattern rather than duplicating near-identical form classes for add versus edit.

Conditional defaults based on context

$form['contact_email'] = [
  '#type' => 'email',
  '#title' => $this->t('Contact email'),
  '#default_value' => $this->currentUser()->isAuthenticated()
    ? $this->currentUser()->getEmail()
    : '',
];

Pre-filling a known value (a logged-in user's own email) saves genuine friction — a small but real usability improvement over always starting blank.

Quick check: your settings form reads $config->get('items_per_page') for a value that's never actually been saved yet. What does that return, and why does the ?? 10 fallback matter? If you said it returns NULL, and without the fallback the field would render with no default at all rather than a sensible starting value, you've understood exactly why that null-coalescing pattern is standard practice.

Key takeaways

  • Real-world defaults usually come from configuration or an existing entity, not a hardcoded literal.
  • An optional entity parameter with a NULL default lets one form class serve both add and edit use cases.
  • Always provide a fallback (??) for config-sourced defaults, since a never-set config value returns NULL.

Coming up next

Defaults control initial values. Next, controlling an element's actual HTML output — CSS classes and attributes.