Webform handlers in Drupal 8/9

Published by Alan Saunders on Sunday, March 14, 2021 - 10:00

Reading Time: 5 minutes




In this blog post, we're going to look at custom webform handlers.

In Drupal there is a module aptly called 'webform', this allows admins and other permitted users to create forms with the fields and settings that they want and set who they want the submission data to be sent too etc.

The Webform module provides a section in the settings for each webform, where the handlers can be set, these handlers are what handles the data in the way that we want it too, whether it be to send the contents to an email address or to a slack channel or somewhere else.

When we have added our custom handler in code, then we should be able to add this custom handler to any custom webforms via the webform ui.

The [module name].info.yml file


As with most of the previous tutorials, we'll need a folder in the custom modules folder, in our case we have a folder called 'custom_webform_handler', in this folder we have a file called with the following contents.

This will enable us to see and install the module within Drupal, we have also set a dependency of the webform module to ensure that this module is installed too.

name: Custom Webform Handler
type: module
description: A module containing a custom webform handler
core_version_requirement: ^8 || ^9
package: Custom
  - 'webform:webform'

Webform Handler class


Now we need to create our class file that will contain our code for our custom webform handler. First we create the folders src/Plugin/WebformHandler and create our class file inside the WebformHandler folder. Our class should extend one of the Webform handler classes, in our case we are extending the WebformHandlerBase class. The important element of the class is the annotation that goes above the opening class tag.

This annotation will tell Drupal and specifically the webform module what we are doing. In this annotation, we'll give the handler an; id, label, category and description, not all of these should be needed, but they should help when trying to find the custom webform handler in the webform UI.

Below is the class that I have put together for the purposes of this tutorial. There are a lot more functions and whatnot that you can add, but for the purposes of this blog post, I just wanted to run some logic when the webform is submitted. As you will notice I am fetching other services like the config factory using dependency injection.

The function defaultConfiguration allows you to configure any default configuration (typically grabbing data from Drupals configuration management) that you may want your webform handler to use when the module is enabled and the handler is added to a webform.

I have deliberately left most of logic out of the submitForm function, so that this more of a generic blog post, as you can do any number of things in this function really with the data from the webform submission within reason of course.


namespace Drupal\custom_webform_handler\Plugin\WebformHandler;

use Drupal\Core\Annotation\Translation;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\webform\Annotation\WebformHandler;
use Drupal\webform\Plugin\WebformHandler\EmailWebformHandler;
use Drupal\webform\Plugin\WebformHandlerBase;
use Drupal\webform\Plugin\WebformHandlerInterface;
use Drupal\webform\Plugin\WebformHandlerMessageInterface;
use Drupal\webform\WebformSubmissionConditionsValidatorInterface;
use Drupal\webform\WebformSubmissionInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

 * Form submission handler.
 * @WebformHandler(
 *   id = "custom_webform_handler",
 *   label = @Translation("Custom webform handler"),
 *   category = @Translation("Slack"),
 *   description = @Translation("Sends submission data to Slack."),
 *   cardinality = \Drupal\webform\Plugin\WebformHandlerInterface::CARDINALITY_SINGLE,
 *   results = \Drupal\webform\Plugin\WebformHandlerInterface::RESULTS_PROCESSED,
 * )
final class CustomWebformHandler extends WebformHandlerBase {
   * @var ConfigFactoryInterface
  protected $configFactory;

   * @var WebformSubmissionConditionsValidatorInterface
  protected $conditionsValidator;

   * @var EntityTypeManagerInterface
  protected $entityTypeManager;

   * @param array $configuration
   * @param $plugin_id
   * @param $plugin_definition
   * @param LoggerChannelFactoryInterface $logger_factory
   * @param ConfigFactoryInterface $config_factory
   * @param EntityTypeManagerInterface $entity_type_manager
   * @param WebformSubmissionConditionsValidatorInterface $conditions_validator
  public function __construct(
    array $configuration,
    LoggerChannelFactoryInterface $logger_factory,
    ConfigFactoryInterface $config_factory,
    EntityTypeManagerInterface $entity_type_manager,
    WebformSubmissionConditionsValidatorInterface $conditions_validator
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->loggerFactory = $logger_factory->get('custom_webform_handler');
    $this->configFactory = $config_factory;
    $this->conditionsValidator = $conditions_validator;
    $this->entityTypeManager = $entity_type_manager;

   * @param ContainerInterface $container
   * @param array $configuration
   * @param string $plugin_id
   * @param mixed $plugin_definition
   * @return ContainerFactoryPluginInterface|EmailWebformHandler|WebformHandlerBase|WebformHandlerInterface|WebformHandlerMessageInterface|static
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(

   * {@inheritdoc}
  public function defaultConfiguration() {
    return [];
   * {@inheritdoc}
  public function submitForm(array &$form, FormStateInterface $form_state, WebformSubmissionInterface $webform_submission) {
    // In here, we perform our logic to manipulate and use the webform submission data however we want.
    // To access data from the webform submission, we call $webform_submission->getData(), we should be able to grab a part of the array that should be returned using a key.
    // The key will be the machine name of the field on the webform. 
    // So for example, if you have a field on the webform with a machine name of group, you code to get the value would be $webform_submission->getData()['group']


Now that we have added our webform handler, we should now be able to add to the handler to our webform.

First navigate to the webform listing page (under structure) and go to the settings page for the webform that you want to add the handler too.

You should see the screen similar to the below screenshot.

Contact webform settings screen

You should now see a link for 'Emails / Handlers', if you click that, you should see a page that looks mostly like the one in the below screenshot.

Contact webform email / handlers screen

Now click the button for 'Add Handler' and you should see a modal window that looks like the one in the below screenshot. Now look for your custom handler. If you don't see it, then check the custom module is enabled and that you have cleared Drupal's cache for good measure.

Contact webform add handler modal screen

Once you have found you custom handler, you can go ahead and click the 'Add Handler' button next to your custom handler. You should now see a sidebar panel show up that should look very similar to the one in the below screenshot.

This will allow you to alter the configuration of the handler, if you have added a custom form to your handler, then in theory this should show somewhere in this window.

Configuration form for the new webform handler

Once you are happy with the configuration, you can now click the 'Save' button. If all has gone well, you should see your handler listed on the emails/ handlers page in the settings for the webform. Just like what is shown in the below screenshot.

Contact webform email / handlers screen after a webform handler has been added.

Related blog posts

Building a Drupal 8/9 custom block

Authored by Alan Saunders on Sunday, January 3, 2021 - 10:00
Reading Time: 6 minutes

Creating custom tokens in Drupal 8/9

Authored by Alan Saunders on Sunday, January 17, 2021 - 10:00
Reading Time: 8 minutes

Creating a custom theme in Drupal 8/9

Authored by Alan Saunders on Tuesday, April 13, 2021 - 23:00
Reading Time: 7 minutes