Form fields created via Ajax are missing element attributes I have a custom form with a date field and a series of textfields and number fields. The textfields and number fields are dynamically re-created via an ajax callback function that runs whenever the date field changes, because the default values of the textfields and number fields will depend on the value of the date field.

The form runs this function once upon loading, which works fine, it returns the $form object with all expected values in the fields.

However, whenever that same function is called again by the ajax trigger, almost all of the attributes of the dynamic fields are missing. The data-drupal-selector, id, name, and value attributes are gone. The default values do not appear. The for attribute of labels also disappears.

I have noticed that this is not true of table form API elements and only seems to affect input elements and labels.

Is this the expected behaviour? Is it avoidable?

Example:

public function buildForm(array $form, FormStateInterface $form_state) {
    $form['date'] = array(
        '#type' =&gt; 'date',
        '#ajax' =&gt; [
            'callback' =&gt; '::dateCallback',
            'wrapper' =&gt; 'wrapper',
        ],
        '#default_value' =&gt; date("Y-m-d")
    );
    $form['wrapper'] = [
        '#type' =&gt; 'container',
        '#attributes' =&gt; ['id' =&gt; 'wrapper'],
    ];
    $this-&gt;dateCallback($form, $form_state);
    return $form;
}

/**
 * Implements callback for Ajax event on date selection.
 *
 * @param array $form
 *   From render array.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   Current state of form.
 *
 * @return array
 *   Date selection section of the form.
 */
public function dateCallback(array &amp;$form, FormStateInterface $form_state) {
    //insert logic to determine value of a variable called $field1    

    $form['wrapper']['options'] = array(
        '#type' =&gt; 'fieldset',
        '#attributes' =&gt; array('class' =&gt; array('options'))
    );
    $form['wrapper']['options']['field1'] = array(
        '#type' =&gt; 'textfield',
        '#title' =&gt; "Field 1",
        '#default_value' =&gt; $field1
    );
    $form['wrapper']['table'] = array(
        '#type' =&gt; 'table',
        '#title' =&gt; t('Table'),
        '#header' =&gt; array("H1", "H2", "H3"),
        '#attributes' =&gt; array('class' =&gt; array('custom-table'))
    );
    //insert logic to generate table rows
    return $form['wrapper'];
}


(Note: In order to get ajax to work with date fields, as per my previous question, I had to apply a Drupal patch. However, I am pretty sure this issue is not related to this patch, as the problem persists even if I attach the callback to another form APi element like a Select dropdown.)
It seems like a good idea to invoke the callback in the form build to include the form elements of the callback in $form to allow the form builder to process these elements. But then you overwrite the same form elements that the form builder has processed with the unprocessed form elements when the ajax triggers the callback.

Solution: Move all form elements in buildForm(). Leave the callback empty, only return the wrapper part of the form.