Symfony-Twig: insert fontawesome icon in a form_widget

sdespont's answer is the correct answer and worthy of being selected. I have, however, extended the functionality of it to include adding the custom fa class icon as well as whether the icon is placed to the left or right of the button text.

Since this functionality accepts variables the best thing to do is to create a template to be reused instead of customising just the view.

Form template: app/Resources/views/form/submit.html.twig

{# app/Resources/views/form/submit.html.twig #}

{% block submit_widget %}
{% set type = type|default('submit') %}

{% if label is empty %}
    {% if label_format is not empty %}
        {% set label = label_format|replace({
                '%name%' : name,
                '%id%' : id,
            }) %}
    {% else %}
        {% set label = name|humanize %}
    {% endif %}
{% endif %}
<button type="{{ type|default('button') }}" {{ block('button_attributes') }}>
    {% if fa is defined %}
        {% if left is defined and left %}
            <i class="fa {{ fa }}"></i> 
        {% endif %}
        {{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }} 
        {% if right is defined and right %}
            <i class="fa {{ fa }}"></i> 
        {% endif %}
    {% else %}
        {{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}
    {% endif %}
</button>
{% endblock submit_widget %}

Controller:

$form = $this->createFormBuilder($user)
            ...
            ->add('submit', SubmitType::class, array(
                'attr'=> array('class'=>'myclass')
            ))
            ->getForm();

Twig template:

{{ form_widget(form.submit, {'fa' : 'fa-long-arrow-right','right' : true}) }}

You can set any old fa icon and even sizing like so: fa-long-arrow-right fa-2x


The easiest, you can put your button with html and form vars:

<button type="submit" name="{{ form.send.vars.full_name }}" id="{{ form.send.vars.id }}" class="btn btn-sm btn-danger"><i class="fa fa-envelope-o"></i></button><

I would define a new form template in the same view (or in a template if you need to reuse the code). More details here

{% extends '::base.html.twig' %}

{% form_theme form _self %}

{%- block submit_widget -%}
    {%- set type = type|default('submit') -%}

    {%- if label is empty -%}
        {%- if label_format is not empty -%}
            {% set label = label_format|replace({
                '%name%': name,
                '%id%': id,
            }) %}
        {%- else -%}
            {% set label = name|humanize %}
        {%- endif -%}
    {%- endif -%}
    <button type="{{ type|default('button') }}" {{ block('button_attributes') }}>
        <i class="fa fa-envelope-o"></i>
        {{ label|trans({}, translation_domain) }}
    </button>
{%- endblock submit_widget -%}


{% block content %}
    {# ... render the form #}

    {{ form_row(form.age) }}
{% endblock %}

EDIT

You can also extend ButtonType to allow icon_before and icon_after in order to add icons easily in form definition :

$form->add('submitReportV2Show', SubmitType::class, array(
    'label' => 'My test', 
    'icon_before' => 'fa-refresh', 
    'icon_after' => 'fa-refresh', 
    'attr' => array('class' => 'btn btn-sm btn-success'
)));

Create a new class src/bundle/Form/Extension:

namespace YourBundle\ToolBoxBundle\Form\Extension;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;

class IconButtonExtension extends AbstractTypeExtension
{
    public function getExtendedType()
    {
        return ButtonType::class;
    }

    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        $view->vars['icon_before'] = $options['icon_before'] ?? '';
        $view->vars['icon_after'] = $options['icon_after'] ?? '';
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'icon_before' => null,
            'icon_after' => null
        ]);
    }
}

Declare it in service src/bundle/Resources/config/service.yml

bundle.tools.form.type_extension.icon_button:
    class: YourBundle\ToolBoxBundle\Form\Extension\IconButtonExtension
    tags:
      - { name: 'form.type_extension', extended_type: 'Symfony\Component\Form\Extension\Core\Type\ButtonType' }

app/Resources/views/Form/fields.html.twig

{%- block button_widget -%}
    {%- if label is empty -%}
        {%- if label_format is not empty -%}
            {% set label = label_format|replace({
                '%name%': name,
                '%id%': id,
            }) %}
        {%- elseif label is same as(false) -%}
            {% set translation_domain = false %}
        {%- else -%}
            {% set label = name|humanize %}
        {%- endif -%}
    {%- endif -%}

    <button type="{{ type|default('button') }}" {{ block('button_attributes') }}>
        {% if icon_before is defined and icon_before is not null %}
            <i class="fa {{ icon_before }}"></i>
        {% endif %}
        {{ translation_domain is same as(false) ? label : label|trans({}, translation_domain) }}
        {% if icon_after is defined and icon_after is not null %}
            <i class="fa {{ icon_after }}"></i>
        {% endif %}
    </button>
{%- endblock button_widget -%}

You can just add a new custom service css class per icon

/* 
 * css selector for a class attribute that starts with "btn-fa-" or has " btn-fa-" in it:
 */
[class^="btn-fa-"]:before,
[class*=" btn-fa-"]:before
{
    font-family: "Font Awesome 5 Free";
    font-weight: bold;
    margin: 0 6px 0 2px;
}

/*
 * And then only 1 setting per font awesome class
 */
.btn-fa-plus:before {
    content: '\f067';
}

And add the class to the ButtonType

->add('Add an item', ButtonType::class, [
    'attr' => [
        'class' => 'btn btn-primary btn-fa-plus',
    ]
])