Introduction

In this post, we will see how to theme form and its fields. Assumming we are having a content type: Article, and it is having following fields:

  • Title
  • Body
  • Tags

Method-1 Theme Fields by their individual Twig file

In this method, we will see over-riding twig files. And, will put some css classes over form fields.

You need to do some pre-requisites here:

  1. Define a method in your theme so that drupal can give you more suggestions for form twig file.
function <theme_name>_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
  if ($hook == 'form' & !empty($variables['element']['#id'])) {
          $suggestions[] = 'form__' . str_replace('-', '_', $variables['element']['#id']);
  }
}
  1. Enable twig debug from your services.yml You can see the name of twig files from where the output is coming and it also gives you the file suggestions names.

Now open your form page, and inspect or view its html. Near the form, you will see something like below:

Drupal twig debug

As we can see that the form twig is coming from core/themes/classy/templates/form/form.html.twig, and the suggestion twig file is form--node-article-form.html.twig

You can simply copy the form.html.twig file from classy folder and copy into your theme’s templates folder. I put it in templates/form/

Its content looks like:


<form{{ attributes }}>
  {{ children }}
</form>

Note: With this, you can not reorder or theme individual fields. For this, see method-2.

Now, lets say we want to theme title field. In html, we can see its output is coming from core/themes/classy/templates/form/input.html.twig, and its suggestions are input--textfield.html.twig.

I’m using bootstrap theme. So, my modified twig file looks like:


{%
  set classes = [
    'form-control rounded'
  ]
%}
<input{{ attributes.addClass(classes) }} />{{ children }}

Similarly for submit button, I copied template file input.html.twig to input--submit.html.twig, and its content looks like:


{%
  set classes = [
    'btn btn-secondary mb-4 w-100 ml-0 mt-2'
  ]
%}
<input{{ attributes.addClass(classes) }} />{{ children }}

My final form twig file looks like:


<div class="border-top border-bottom bg-white mt-4" id="lead">
  <div class="row">
    <div class="col-8 mx-auto">
      <form{{ attributes }}>
        {{ children }}
      </form>
    </div>
  </div>
</div>

Output

Drupal twig form

Method-2

In above method, you can theme form and its individual elements. But, you can not theme the ordering of form elements. Example: You want to show your form in two columns.

Lets take a look.

Goto your theme file: life2.theme (life2 is name of my theme)

function life2_theme($existing, $type, $theme, $path) {
  return array(
    'node_article_form' => array(
      'render element' => 'form',
      'template' => 'form/form--story-write-form',
    ),
  );
}

function life2_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  if ($form_id === 'node_article_form') {
    $form['#theme'] = 'node_article_form';
  }
}

With above code, you can also specify your desired template filename. Lets see content of form--story-write-form


<form{{ attributes }}>
  <div class="border-top border-bottom bg-white mt-4" id="lead">
    <div class="row">
      <div class="col-8 mx-auto">
        {{ form.title }}
        {{ form.body }}
        {{ form |without('title')|without('body')|without('field_tags')}}
      </div>
      <div class="col-4 mx-auto">
        {{ form.field_tags }}
      </div>
    </div>
  </div>
</form>

Output

Drupal themed Form output

You can ofcourse have more beautiful design.

I hope it helps.