I’m having trouble getting Contact Form to work when posting through AJAX.


{% macro errorList(errors) %}
    {% if errors %}
        <ul class="errors">
            {% for error in errors %}
                <li>{{ error }}</li>
            {% endfor %}
    {% endif %}
{% endmacro %}

{% from _self import errorList %}

<div id="my-form" class="form">

    <form method="post" action="" accept-charset="UTF-8">
    {{ getCsrfInput() }}
    <input type="hidden" name="action" value="contactForm/sendMessage">
    <input type="hidden" name="redirect" value="contact/thanks">

      <div class="grid-container">
        <div class="grid-x grid-padding-x">
          <div class="medium-6 cell">
            <h3><label for="fromEmail">Your Email</label></h3>
                <input id="fromEmail" type="email" name="fromEmail" value="{% if message is defined %}{{ message.fromEmail }}{% endif %}">
                {{ message is defined and message ? errorList(message.getErrors('fromEmail')) }}
          <div class="medium-12 cell">
            <h3><label for="message">Message</label></h3>
                <textarea rows="10" cols="40" id="message" name="message">{% if message is defined %}{{ message.message }}{% endif %}</textarea>
                {{ message is defined and message ? errorList(message.getErrors('message')) }}

                <input type="submit" value="Send">
          <div id="thanks" style="opacity:0">THANKS</div>



$('#my-form').submit(function(ev) {
    // Prevent the form from actually submitting

    // Get the post data
    var data = $(this).serialize();

    // Send it to the server
    $.post('/', data, function(response) {
        if (response.success) {
        } else {
            // response.error will be an object containing any validation errors that occurred, indexed by field name
            // e.g. response.error.fromName => ['From Name is required']
            alert('An error occurred. Please try again.');

In this case the response is not a success so the alert is triggered.

Out of interest I console logged the response in the else block and it’s all the HTML on the page. And then I thought, why don’t I console log the data variable right after it’s declared and - same thing - it’s all the HTML on the page.

Anybody got any tips?

1 Answer 1


This is your html

<div id="my-form" class="form">

And This is your js event handler

$('#my-form').submit(function(ev) {

So $(this) is actually a <div> container element and $(this).serialize() is null so you'll send no data. You should add a id to your form

<form method="post" action="" id="myForm" accept-charset="UTF-8">

and send the form data instead of a div data.

var data = $("#myForm").serialize();

I reproduced it, and it worked - even though I would prefer sending an actual object instead of a serialized form, but that's only subjective

  • Also remove the id="my-form" from the div! By the way, the reason you were getting a full html page response was because craft didn't know where to route the request due to the form data missing, so it displayed an error page (the html you received). A good next step would have been to check the request headers to see what was being posted to where. Commented Nov 17, 2017 at 2:35
  • Thanks, all. I'm a bit ashamed that I didn't pick up on this and deeply appreciate your assistance! Commented Nov 17, 2017 at 16:42

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.