How to render a form in TWIG Drupal 8

In some situations, we need to manage the structure of the fields in a custom form; this management makes it easier for the Frontend to style the form structures. So, if what we want is to render or display each specific field of a form in a custom TWIG template, these are the steps:

For this example, we'll use a dummy module: modules/custom/example_fields_form

Creating the module example_fields_form.info.yml:

type: module
name: example_fields_form
description: 'some description'
core: 8.x
 

With our module in place, we need to create a routing file, which will be responsible for calling and executing our form with a basic content access requirement. To do this, we will create the file example_fields_form.routing.yml:

example_fields_form.fields_form:
  path: 'example-fields-form'
  defaults:
    _title: 'example_fields_form'
    _form: '\Drupal\example_fields_form\Form\BuildingForm
  requirements:
    _permission: 'access content'
 

If we look at the _form route located in the module, we have given it a path /Form/class_name. Since we have defined this route, the routing file will look for it, so we need to create the structure from the module's root folder as follows: /example_fields_form/src/Form/BuildingForm.php. We created the /src and /Form directories, and the file should have the following structure:

<?php
/**
 * @file
 * Contains \Drupal\example_fields_form\Form\BuildingForm.
 */
namespace Drupal\example_fields_form\Form;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\FormBase;

class BuildingForm extends FormBase {

    public function getFormId() {
        return ‘example_fields_form’;
    }

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

    public function submitForm(array &$form, FormStateInterface $form_state) {
        drupal_set_message($this->t('@can_name ,Your application is being submitted!', array('@can_name' => $form_state->getValue('candidate_name'))));
         foreach ($form_state->getValues() as $key => $value) {
           drupal_set_message($key . ': ' . $value);
         }
    }

    public function validateForm(array &$form, FormStateInterface $form_state) {
        $values = $form_state->getValues();
    }
}

At this point, we already have the basic structure to build our form and the basic submit and validate functions. Now, we only need to add fields to our buildForm function. For this, we will rely on the documentation from drupal.org: https://api.drupal.org/api/drupal/elements/8.2.x For this example, we will use an email field and a button:

$form['email'] = array(
            '#type' => 'email',
            '#placeholder' => t('Email'),
            '#required' => TRUE,
);

//Botón
$form['actions']['continue'] = array(
    '#type' => 'submit',
    '#value' => $this->t('continue'),
    '#attributes' => ['class' => ['act-continue']],
);

Now we need to tell this form that it will have a #theme property in order to render our elements. For that, we add:

$form['#theme'] = 'example-fields-form';
 

The name we choose is the same one we will use to name our template file.

Now, if we need to add our custom style files for this module, we include:

$form['#attached']['library'][] = example_fields_form/example_fields_form';
 

With this and having the corresponding directory structure and the file example_fields_form.libraries.yml as follows:

example_fields_form':
  version: 1.x
  js:
    js/main.js: {}
  css:
    theme:
      css/main.css: {}
Lastly, we return the form.
 
return $form;

Now we need to create our template. To do this, we will create a new directory from the root folder of the module as follows: /example_fields_form/templates. With the directory created, we will create our file named as we did in the form: example-fields-form.html.twig. Now we have our file created.

To link our Form with the Template, we will use a hook_theme. For this, we need to create a file named example_fields_form.module from the root folder, and it should look like this:

function example_fields_form_theme($existing, $type, $theme, $path) {
    return [
        'example-fields-form' => [
            'render element' => 'form',
        ],
    ];
}
We can now use our form in the .twig file. To do this, we will call the fields we created in the example-fields-form.html.twig file as follows:
 
<article class="example_fields_form">
    <section>
      <div class="content">
        {{ form.email }}
        {{ form.actions.continue }}
      </div>
    </section>
</article>
{{ form.form_build_id }}
{{ form.form_token }}
{{ form.form_id }}
 

It is important to call these last fields so that the form can be identified and return all its data.

I hope this helps you.