Drupal Form API: Vertical Tabs

How to programmatically create custom Drupal forms with vertical tabs.

Steps involved

  1. Create grouping elements (fieldset or details).
  2. Place fields in the grouping elements.
  3. Create a vertical tabs bar.
  4. Place the grouping elements in the tabs bar.

Create grouping elements and insert form fields

Even though there exists a fieldset render element ('#type' => 'fieldset') the Drupal fieldsets documentation recommends using the details render element ('#type' => 'details') if „you are looking to visually organize a group of form elements“. As far as this example is concerned, both types of fields can be used equally.

public function buildForm(array $form, FormStateInterface $form_state) {
    // Create a grouping element using a fieldset.
    $form['demofieldset'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('A fieldset'),
      // Show collapse icon.
      '#collapsible' => TRUE,
      // Open by default.
      '#collapsed' => FALSE,
    ];

    // Create a grouping element using a details element.
    $form['demofieldset2'] = [
      '#type' => 'details',
      '#title' => $this->t('A details element'),
      // Open by default.
      '#open' => TRUE,
    ];

    // Place a textfield in the first fieldset.
    $form['demofieldset']['field1'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Textfield 1'),
    ];

    // Place a textfield in the details element.
    $form['demofieldset2']['field2'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Textfield 2'),
    ];
}

Depending on your theme, the output (visually as well as the code) might be the same for both types of form fields.

Show the grouping elements as vertical tabs

Now the grouping elements created above can be displayed as tabs of a vertical tab bar. Create a Vertical Tabs element and assign it's ID to the grouping elements.

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

    $form['verticaltabs'] = [
      '#type' => 'vertical_tabs',
      // Open the second tab by default.
      // Find the target element's ID by using your browser's debugger.
      //'#default_tab' => 'edit-demofieldset2',
    ];

    // Create a grouping element using a fieldset.
    $form['demofieldset'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('A fieldset'),
      // Show collapse icon.
      '#collapsible' => TRUE,
      // Open by default.
      '#collapsed' => FALSE,
      // Place this group in the vertical tabs bar.
      '#group' => 'verticaltabs',
    ];

    // Create a grouping element using a details element.
    $form['demofieldset2'] = [
      '#type' => 'details',
      '#title' => $this->t('A details element'),
      // Open by default.
      '#open' => TRUE,
      // Place this group in the vertical tabs bar.
      '#group' => 'verticaltabs',
    ];

    // Place a textfield in the first fieldset.
    $form['demofieldset']['field1'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Textfield 1'),
    ];

    // Place a textfield in the details element.
    $form['demofieldset2']['field2'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Textfield 2'),
    ];
}

This will display a vertical tab bar with two tabs. The grouping elments' titles are used as tab labels.

Further reading