A settings form that always opens blank is broken in a subtle way — visitors can't tell what's currently configured versus what they're about to change. Retrieving configuration data is what makes a form reflect reality: read the saved value inside buildForm(), before the form is even rendered, and hand it to the element as its #default_value.
What you'll learn in this lesson
- Where to read config from — always inside
buildForm(), before the elements are defined - The difference between loading config as read-only versus editable
- How
#default_valueconnects a stored setting to a rendered form field - What happens the first time a form loads, before any value has ever been saved
Retrieving config inside buildForm()
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$config = $this->config('config_simple_example.settings');
$form['message'] = [
'#type' => 'textarea',
'#title' => $this->t('Message'),
'#default_value' => $config->get('message'),
];
return parent::buildForm($form, $form_state);
}
How it works
Loading config read-only vs. editable
$this->config('config_simple_example.settings') inside buildForm() loads the config object in its normal, cached, read-only mode — appropriate here, since you're only displaying a value, not changing it. This is the same call used in submitForm(), but there it's implicitly editable because a subsequent set() is about to be chained onto it. Reading and writing use the identical entry point; the difference is only in what you do with the object afterward.
get('message') — reading one key
$config->get('message')
get() reads exactly one key from the config object and returns its current value, cast to whatever type the schema declared (recall from the schema lesson: message is typed text). You can call get() with no argument at all to retrieve the entire config object as an array, useful when a form needs to read several keys at once.
Connecting the value to the form via #default_value
$form['message'] = [
'#type' => 'textarea',
'#default_value' => $config->get('message'),
];
#default_value is the standard Form API property that pre-fills any element with a starting value — nothing config-specific about the property itself. What makes this a "config form pattern" is simply that the value being fed into it came from $config->get() rather than a hardcoded default or a database query.
get('message') returns whatever config/install/config_simple_example.settings.yml shipped as the default — 'Awesome settings' in this real example. That's exactly why every module with a config form should also ship a matching config/install/*.yml file: without one, the form's first load shows an empty field instead of a sensible starting value.Quick check: why does
buildForm()call$this->config()again, instead of reusing whateversubmitForm()loaded on a previous request? (Answer: each HTTP request is stateless and starts fresh —buildForm()runs on every page load of the form, including the very first one, so it always re-reads from the actual saved config rather than relying on some value carried over from a different request.)
Key takeaways
- Read config for display inside
buildForm(), using the same$this->config($name)helper as the write side. get('key')returns one typed value;get()with no argument returns the whole config object as an array.#default_valueis the standard Form API bridge between a saved config value and a rendered element — no special config-only property exists.- Always ship a matching
config/install/*.ymlfile so a fresh install's form shows a sensible default rather than an empty field.
Coming up next
Reading and writing config safely also means rejecting bad input before it's ever saved — the next lesson, "Validating Configuration Forms," covers exactly that.