How can I add a custom form submission handler?

I tried adding $form['#submit'][] = 'mymodule_form_submit'; or $form['actions']['submit']['#submit'][] = 'mymodule_form_submit'; to hook_form_alter().

use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\HttpFoundation\Request;

function MYMODULE_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if ($form_id == 'node_trends_form' || $form_id == 'node_trends_edit_form') {
    foreach (array_keys($form['actions']) as $action) {
      if ($action != 'preview' && isset($form['actions'][$action]['#type']) && $form['actions'][$action]['#type'] === 'submit') {
        $form['actions']['submit']['#submit'][] = 'mymodule_form_submit';
function mymodule_form_submit(array $form, FormStateInterface $form_state){
    //die("why won't this execute? :(");
    drupal_set_message("Why won't this message show?");

It seems drupal_set_message() is not called. I also tried rebuilding the cache, but the function is still not called.

I am using Drupal 8.2.3.

  • You should show the full implementation of hook_form_alter(), or it's a bit harder to tell you what you are doing wrong. Also, you should say which form you are trying to alter.
    – apaderno
    Commented Dec 19, 2016 at 16:51
  • @kiamlaluno I add my hook_form_alter.
    – Yuseferi
    Commented Dec 19, 2016 at 16:56
  • what's your code still $form['actions']['submit']['#submit']? Try replace ['submit'] with [$action].
    – MrD
    Commented Dec 19, 2016 at 17:10

3 Answers 3


If you use hook_form_node_form_alter() maybe use example code:

function MYMODULE_form_node_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  foreach (array_keys($form['actions']) as $action) {
    if ($action != 'preview' && isset($form['actions'][$action]['#type']) && $form['actions'][$action]['#type'] === 'submit') {
      $form['actions'][$action]['#submit'][] = 'mymodule_form_submit';

and for the submit function use:

function MYMODULE_form_submit(array $form, \Drupal\Core\Form\FormStateInterface $form_state){
  //die("I'm not getting run, why :(");
  \Drupal::service('messenger')->addMessage("Why won't this message show?");

Creating a custom webform handler plugin for drupal 8.

This document assumes you've already installed and enabled webform and webform-ui

1) Create your webform. - Go to structure -> webforms and press the "+ Add webform" button. - You can either use the ui or use yaml, that's up to you. example yaml for a one field form that takes an email address:

  '#type': email
  '#title': email
  '#title_display': invisible
  '#placeholder': 'ENTER YOUR EMAIL'
      - my-ip

The indentation is important for yaml so make sure you get it right. indents are spaces.

Now save your form.

2) Creating a webform handler plugin

Next we can create a new plugin which will show up in the "Emails/Handlers" section when editing the webform. I will call it myhandler, you can call it what you want, provided you replace all mentions of myhandler with the name you pick.

a) Create a new folder for your plugin, do this in your drupal root (referred to here as /var/www/html/) in the following subfolder: /var/www/html/modules/Custom/myhandler

b) Create a new file in the above directory called myhandler.info.yml in this file goes the following:

name: My Form Handler
description: handles form submits, does something with them. 
package: Custom
type: module
version: 1.0
core: 8.x

3) Create a src directory in your module directory, eg: /var/www/html/modules/Custom/myhandler/src in src create Plugin in Plugin create WebformHandler

(this can be achived in one go using

mkdir -p /var/www/html/modules/Custom/myhandler/src/Plugin/WebformHandler/ 

which will make the entire structure in one go using the -p flag to mkdir.)

4) Create a new file /var/www/html/modules/Custom/myhandler/src/Plugin/WebformHandler/MyFormHandler.php

in that file goes the following php code, i've left the config form setup in so you can see how to configure your plugin if needed.

namespace Drupal\myhandler\Plugin\WebformHandler;

use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Serialization\Yaml;
use Drupal\Core\Form\FormStateInterface;
use Drupal\webform\Plugin\WebformHandlerBase;
use Drupal\webform\webformSubmissionInterface;

 * Form submission handler.
 * @WebformHandler(
 *   id = "myhandler_form_handler",
 *   label = @Translation("MyHandler form handler"),
 *   category = @Translation("Form Handler"),
 *   description = @Translation("Do something extra with form submissions"),
 *   cardinality = \Drupal\webform\Plugin\WebformHandlerInterface::CARDINALITY_SINGLE,
 *   results = \Drupal\webform\Plugin\WebformHandlerInterface::RESULTS_PROCESSED,
 * )
class MyFormHandler extends WebformHandlerBase {

       * {@inheritdoc}

     public function defaultConfiguration() {
        return [
            'submission_url' => 'https://api.example.org/SOME/ENDPOINT',

     * {@inheritdoc}
    public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
        $form['submission_url'] = [
            '#type' => 'textfield',
            '#title' => $this->t('Submission URL to api.example.org'),
            '#description' => $this->t('The URL to post the submission data to.'),
            '#default_value' => $this->configuration['submission_url'],
            '#required' => TRUE,
        return $form;

   * {@inheritdoc}
  public function submitForm(array &$form, FormStateInterface $form_state, WebformSubmissionInterface $webform_submission) {
    // Your code here.
        // Get an array of the values from the submission.

        $values = $webform_submission->getData();

        // Get the URL to post the data to.
        $post_url = $this->configuration['submission_url'];

        $message = "MyHandler got form data:".print_r($values,1); 

        return true;

5) Enable your MyHandler module (using drush or extend menu) and then rebuild your drupal cache ( "drush cr" from anywhere under your drupal root (/var/www/html here) would do that if you use drush )

6) edit your webform, go to "Email / Handlers" and click on "+ Add Handler" button You should see your plugin listed, click Add Handler, you should now see a box asking for the submission url. click Save button. If something doesn't look right, or it isn't working, check the apache error log, you might find something helpful there.

7) Test your form - make a submission to the form, and then check the watchdog log (drush ws), you should see the values sent to it. They might be truncated in the output you see, don't panic, it's all there. What you do with this now is up to you.

Hope this helps someone. I cobbled it together from stuff I found around the place and wrote it into a single document. Thanks to the others who got me here.

  • 2
    If I get it right, the one minor thing that's missing in the above code for the handler to work correctly is the submitConfigurationForm() method, consisting of parent::submitConfigurationForm($form, $form_state); and parent::applyFormStateToConfiguration($form_state);.
    – Hendrik
    Commented Mar 31, 2018 at 16:49
  • 1
    @Hendrik No, you don't need it. I've created a new handler with only a submitForm() function and it works. All other functions are situated in the base class and I have no needs to override they. By the way: a quite nice a simple solution once you figure it out ;-)
    – RWAM
    Commented Jul 5, 2018 at 9:49
  • 2
    This is a great comment, but it doesn't answer @Yuseferi's question since his question is about Drupal form handling in general, not the webform module.
    – apotek
    Commented Dec 31, 2022 at 1:53
  • 1
    Bumped this down as the question isnt relating to webform.
    – Christian
    Commented Oct 9, 2023 at 4:45
  • Many in the global web development industry sometimes mean any “<form> tag appearing in an HTML page” as webform in general – probably to emphasize its contrast against any other kind of implementations of taking data from users (eg. some installed software's built-in input interface). In the narrower scope of Drupal terminology however the terms “webform” and “form” are greatly different, therefore clearly distinguishing them is important to not confuse beginners with this topic. Thus I downrated your answer.
    – Balu Ertl
    Commented Feb 2 at 10:38

if using hook_form_BASE_FORM_ID_alter in Drupal 8 core 8.4.3, I found the ways of adding the custom submit handler didn't work. This worked for adding the submit handler function name:

$form['#submit'][] = 'mymodule_submit_handler';

In another situation, using hook_form_FORM_ID_alter in Drupal 8.4.5, I found the above did not work for adding the custom submit handler. Instead, this worked:

$form['actions']['submit']['#submit'][]  = 'mymodule_submit_handler';
  • 2
    This is true. $form['actions']['submit']['#submit'][] = 'mymodule_submit_handler'; is the only way to get this work.
    – drupalfan
    Commented Jan 28, 2022 at 12:46
  • same thing, need to use $form['actions']['submit']['#submit'][] Commented Feb 8, 2023 at 13:54
  • This is the correct answer. You change the actions on the widget, not the form.
    – Christian
    Commented Oct 9, 2023 at 4:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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