I wan't to alter contact form recipient according to what user chose in one of the dropdown fields of my Contact form.

I implemented hook_form_FORM_ID_alter() in order to add custom submit callback like this

 $form['actions']['submit']['#submit'][] = 'my_custom_callback';

to get submitted data.

I thought I could use State API in my_custom_callback() to store received data temporally and use it's value in hook_mail_alter() but it looks like hook_mail_alter() is called before my custom callback.

This is what the order of callbacks looks like:

  1. Default Submit callback of Contact form;
  2. hook_mail_alter();
  3. my_custom_callback.

I wander if there is any way to change this order to this:

  1. my_custom_callback;
  2. Default Submit callback of Contact form;
  3. hook_mail_alter().

2 Answers 2


Why do you need a submit callback at all? The built contact message is available in hook_mail_alter(), you can just access the submitted data there?

This is from a custom project where we did something similar:

 * Implements hook_mail_alter().
function yourmodule_mail_alter(&$message) {
  if ($message['id'] == 'contact_page_mail') {
    /** @var \Drupal\contact\Entity\Message $contact_message */
    $contact_message = $message['params']['contact_message'];

    if ($contact_message->bundle() != 'feedback') {

    $recipient_address = $contact_message->get('yourfield')->value;
    if (\Drupal::service('email.validator')->isValid($recipient_address)) {
      $message['to'] = $recipient_address;

I would store the relevant values for sending the mail in a temporary table when my_custom_callback is executed, and then process the data in this table using a cron job or batch job. Batch would probably be better - gives you the tools to lock records, etc. You could run the batch using a cron job :)

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.