11

I'd like to theme a contact form in Drupal 8. I'd like to put divs around form elements (using bootstrap).

I also want to put some classes on certain elements (submit button, the form itself).

  • 2
    I suggest describing your issue in more details and saying what have you tried already. That will show some initiative and help others give you a better answer. – Aram Boyajyan May 22 '15 at 7:43
16

Open up your YOURTHEMENAME.theme file and add the following code:

function YOURTHEMENAME_form_contact_message_feedback_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {

  // Name
  $form['name']['#prefix'] = '<div class="row"><div class="col-md-6"><div class="form-group">';
  $form['name']['#suffix'] = '</div>';
  $form['name']['#attributes']['placeholder'][] = $form['name']['#title'].'*';
  $form['name']['#attributes']['class'][] = 'form-control';
  unset($form['name']['#title']);

  // Mail
  $form['mail']['#prefix'] = '<div class="form-group">';
  $form['mail']['#suffix'] = '</div>';
  $form['mail']['#attributes']['placeholder'][] = $form['mail']['#title'].'*';
  $form['mail']['#attributes']['class'][] = 'form-control';
  unset($form['mail']['#title']);


  // Subject
  $form['subject']['widget'][0]['value']['#attributes']['class'][] = 'form-control';
  $form['subject']['widget'][0]['value']['#attributes']['placeholder'][] = $form['subject']['widget'][0]['#title'].'*';
  $form['subject']['widget'][0]['#title'] = '';
  unset($form['subject']['widget'][0]['value']['#title']);
  $form['subject']['widget'][0]['#prefix'] = '<div class="form-group">';
  $form['subject']['widget'][0]['#suffix'] = '</div></div>';

  // Message
  $form['message']['widget'][0]['value']['#attributes']['class'][] = 'form-control';
  $form['message']['widget'][0]['value']['#attributes']['placeholder'][] = $form['message']['widget'][0]['#title'].'*';
  $form['message']['widget'][0]['#title'] = '';
  unset($form['message']['widget'][0]['value']['#title']);
  $form['message']['widget'][0]['#prefix'] = '<div class="col-md-6"><div class="form-group">';
  $form['message']['widget'][0]['#suffix'] = '</div></div></div>';

  // Submit
  $form['actions']['#prefix'] = '<div class="clearfix">';
  $form['actions']['#suffix'] = '</div>';
  $form['actions']['submit']['#attributes']['class'][] = 'btn';
  $form['actions']['submit']['#attributes']['class'][] = 'btn-success';
  $form['actions']['submit']['#attributes']['class'][] = 'pull-right';

}

And here is your result: enter image description here

Don't forget to clear your caches ;)

  • 1
    If my theme's named 'abc', and my form's named 'contact us', what do I name the function? – Daniel Dewhurst Jun 30 '16 at 10:03
  • 3
    {themename}_form_contact_message_{formname}_form_alter – Johan Haest Aug 11 '16 at 7:53
  • This is so awesome!!! – ipwa Apr 12 '17 at 15:12
19

You can simply theme every form how you want. I don't like the method by @rpayanm, cuz it hard to maintane and hard readable, with big forms this is not the case, just single wrapper and simple structure.

Every form trying to use theme equals their machine name. You can find this template name in $form['#theme'] just by alter, it is always (or in most times) same as form id machine name. What you need to do is register template for it. You need to implement hook_theme(). You can write it in CUSTOMMODULE.module or in THEMENAME.theme. Theme key must be the same as in $form['#theme'], so its automatically use it, otherwise you need to add new one via form alter.

/**
 * Implements hook_theme().
 */
function MODULENAME_theme($existing, $type, $theme, $path) {
  return [
    'FORM_ID_THEME' => [
      'template' => 'custom-form-template-name',
      'render element' => 'form',
    ]
  ];
}

Drupal pass $form variable with form.

Then you must create custom-form-template-name.html.twig (with your template name from hook_theme()).

The minimal template is

{{ form }}

You also can print every field from form where you want

{{ form.field_name }}

Here you can do whatever you want with markup.

You also can pass additional data to template by implementing template_preprocess_TEMPALTENAME

function template_preprocess_FORM_ID_THEME(&$variables) {
  $variables['example'] = 'This is added via preprocess';
}

And then use in template

{{ example }}
<div class="first-field">
  {{ form.first_field }}
</div>

<div class="second-field">
  {{ form.second_field }}
</div>

<div class="buttons">
  {{ form.submit }}
  {{ form.preview }}
</div>
{#
Don't forget to add printing form special info at the end.
Without this data form will not working propertly.
#}
{{ form.form_build_id }}
{{ form.form_token }}
{{ form.form_id }}

I think this method is more fliexible, clean and powerful.

P.s. sorry for my english, hopes someone edit and fix my mistakes :)

Example of complex form using this method.

enter image description here

  • 2
    Note that for me, I had change {{ form.submit }} to {{ form.actions.submit }} – mikeDOTexe Oct 24 '16 at 23:19
  • @Niklan: I have followed above same code for my custom entity bundle form. If i use {{ form }}, it is printing entire form.But if i use specific field like {{ form.my_field }}, nothing displayed. Any idea how can we display each fields of custom entity bundle form? – kalidasan Jul 19 '17 at 14:10
  • @kalidasan try to print every field without printing {{ form }}. I suggest after printing {{ form }} all form elements will be marked as '#rendered' => TRUE and won't be rendered soon. If you wan't to use this method of theming form, you must print every field individually. There is no drupal_render_children() in Drupal 8, unless you write it by yourself. Anyway by default this will cause duplicates, so try to print each field manually. – Niklan Jul 19 '17 at 15:32
  • @Niklan: I have not used {{ form }} & {{ form.my_field }} together. First tried with only {{ form }}, here I can able to see entire form. Then removed {{ form }} from template then tried for each form fields separately like this {{ form.my_field }}, etc , but dint get any out put. Nothing displayed. – kalidasan Jul 20 '17 at 6:51
  • 1
    @kalidasan you can unset titles in template_preprocess_FORM_ID_THEME(&$variables). In this preprocess you can add new variables, change existing including form elements. I never seen this {{ form.field_name.widget }} and I theming forms often. Looks like this field is custom created or added by third party module? I think this is allowed, but not rule for others. I recommend you to start with preprocess hook and just look at $variables['form'] what it contains inside. – Niklan Jul 20 '17 at 7:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.