Unlimited Custom Form Fields - Without Ajax I have a simple form where users can set the number of fields they need. Users may need 100+ fields so I am not using ajax "add more" options.

With the below code, everything works (Even though you have to save the form twice, once to add the fields, twice to save the data), but I get an Undefined offset notice Notice: Undefined offset: n anytime I increase the field count and save the form.

As I'm not sure a better way to ask this question... 

1) I know the code below is not perfect UX (having to save a form twice), does Drupal offer any API advantages to my approach I am not aware of?

2) Is there a way to handle/suppress these notices?

public function buildForm(array $form, FormStateInterface $form_state) {

  $config = $this-&gt;config('MYMODULE.settings');

  $form['#tree'] = TRUE;

  $form['fieldset'] = [
    '#type' =&gt; 'fieldset',
  ];

  $form['fieldset']['field_number'] = [
    '#type' =&gt; 'textfield',
    '#default_value' =&gt; $config-&gt;get('field_number') ?: 1,
  ];

  for ($i = 0; $i &lt; $config-&gt;get('field_number'); $i++) {

    $form['fieldset']['field'][$i] = [
      '#type' =&gt; 'textfield',
      '#default_value' =&gt; $config-&gt;get('field')[$i] ?: '',
    ];

  }

  return parent::buildForm($form, $form_state);
}

public function submitForm(array &amp;$form, FormStateInterface $form_state) {
  $values = $form_state-&gt;getValues();

  $this-&gt;configFactory-&gt;getEditable('MYMODULE.settings')
    -&gt;set('field_number', $values['fieldset']['field_number'])
    -&gt;set('field', $values['fieldset']['field'])
    -&gt;save();

  parent::submitForm($form, $form_state);

}
My issue was from improper checking of array key values. Below is the approach for non-ajax form adjustment (which is very similar to ajax).

MYMODULE/src/Form.php:

/**
 * {@inheritdoc}
 */
public function buildForm(array $form, FormStateInterface $form_state) {

  // get form config.
  $config = $this-&gt;config('MYMODULE.settings');

  // Set our count. Used in for loop below.
  if (!empty($form_state-&gt;getValue(['fieldset', 'field_count']))) {
    // get $form_state count value.
    $defaultCount = $form_state-&gt;getValue(['fieldset', 'field_count']);
  }
  else {
    // Get config count value.
    $defaultCount = $config-&gt;get('field_count');
  }

  $form['#tree'] = TRUE;

  // Fieldset item.
  $form['fieldset'] = [
    '#type' =&gt; 'fieldset',
    '#prefix' =&gt; '&lt;div id="fieldset-wrapper"&gt;',
    '#suffix' =&gt; '&lt;/div&gt;',
  ];

  // Field count item.
  $form['fieldset']['field_count'] = [
    '#type' =&gt; 'number',
    '#title' =&gt; 'Number',
    '#required' =&gt; TRUE,
    '#default_value' =&gt; $config-&gt;get('field_count') ?: 1,
  ];

  // Rebuild submit item.
  $form['fieldset']['rebuild'] = [
    '#type' =&gt; 'submit',
    '#value' =&gt; 'Apply number',
    // Make our call to the rebuild function.
    '#submit' =&gt; ['::rebuildFormSubmit'],
  ];

  // Loop through our count and create form items.
  for ($i = 0; $i &lt; $defaultCount; $i++) {

    $form['fieldset']['field'][$i] = [
      '#type' =&gt; 'textfield',
      // This is important. Must check isset() to avoid Undefined index notices.
      '#default_value' =&gt; isset($config-&gt;get('field')[$i]) ? $config-&gt;get('field')[$i] : '',
    ];

  }

  return parent::buildForm($form, $form_state);

}

/**
 * {@inheritdoc}
 */
public function rebuildFormSubmit(array &amp;$form, FormStateInterface $form_state) {
  $form_state-&gt;setRebuild(TRUE);
}


Related question: Get an Un-submitted/Active Form Field Value in $form_state?.