Building an AJAX-based form for Formspree

This post is more than 2 years old.

Back when I converted my site from WordPress to Hugo, one of the issues I had to take care of was setting up a processor for my contact form. I decided to go with Formspree as their free tier easily handled the amount of form submissions I got per month. While most folks will use Formspree with a "regular" old form post, you can also use a fancy-pants Ajax submission as well. The Formspree folks document this, but their example is rather short, and when I was asked by someone on the Surge Slack about a full example, I decided to whip something up.

To be clear, and more on this at the end, this was a quick bit of code just to give that user a "real" example they could take and modify. There's many different ways of doing this and what I've built here was done in about five minutes.

My example consists of two files - an HTML file and a JavaScript file. There's no styling involved but I assume folks can handle that on their own. First, the HTML.


<!DOCTYPE html>
<html lang="en">
  <head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Test FormSpree.io</title>
  </head>
  <body>
	  
	<div id="formBlock">
		<form id="someForm">
			<label for="name">Name:</label> <input type="text" id="name"><br/>
			<label for="email">Email:</label> <input type="email" id="email"><br/>
			<label for="comments">Comments:</label> <br/>
			<textarea id="comments"></textarea><br/>
		<input type="submit">
		</form>
	</div>
	<div id="thankyouBlock" style="display:none">
		<p>
			Thank you for filling out the form. I care a lot about it.
		</p>
	</div>
	
	<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
	<script src="test.js"></script>
	
  </body>

The only thing particularly interesting here is a hidden div used for the "thank you" message. Formspree supports sending you to a "thank you" page when you send a regular form POST to it, but when working with the Ajax version we need to handle this ourselves. I could have also just changed the HTML on the fly and injected a message, but this seemed simplest. My form has 3 fields arbitrarily chosen for the demo.

Now for the JavaScript:


$(document).ready(function() {

	$('#someForm').on('submit', function(e) {
		e.preventDefault();
		
		//get the name field value
		var name = $('#name').val();
		//get the name field value
		var email = $('#email').val();
		//get the comments
		var comments = $('#comments').val();
					
		//pretend we don't need validation
		
		//send to formspree
		$.ajax({
			url:'https://formspree.io/raymondcamden@gmail.com',
			method:'POST',
			data:{
				name:name,
				_replyto:email,
				 email:email,
				comments:comments,
				_subject:'My Form Submission',
			},
			dataType:"json",
			success:function() {
				console.log('success');	
				$('#formBlock').hide();
				$('#thankyouBlock').show();
			}	

		});		
		
	});

});	

So I assume this is pretty vanilla jQuery, and of course, you don't have to use jQuery. I get my data, validate it (well, I wrote a comment saying I would), and then simply POST to Formspree. I do manipulate the data a bit. First, I pass the email value twice. Why? By passing it as _replyto, I can actually reply to the email Formspree sends me with the form contents. (As an FYI, I completely missed the fact that Formspree will treat a field named "email" as the replyto as well. So my code there was unnecessary. This is definitely documented, but I missed it.) I still want to see the address so I include it again. _subject doesn't come from the form at all, but is used by Formspree to set the subject line of the email sent.

While that's it - let's quickly look at what happens when you use this code. First off, don't forget the Formspree requires you to validate a form before it will email you. If you actually look at the result of the POST in your form tools, you'll see this the first time you run it.

Requires confirmation

The good news is that Formspree still sends the email, but it only does so once and you must confirm it. When you deploy this code to your production site, be sure to quickly confirm it. Their email does a good job of making it real clear you darn well better do so:

Confirm or die!

When you do confirm, this is the response JSON you get:

Happy form submission

The Formspree docs don't really describe in what situations you would get an error. You could, I suppose, notice when success isn't there and handle it by telling the user that your forms are currently broken and they should simply email you instead. And of course, it just plain works:

Email of the form

So since this discussion came up on the Surge Slack, I went ahead and Surged it. Because why not? You can run this demo here: http://hospitable-cushion.surge.sh/test.html. I can't promise it will be up forever, but I'll run it for now.

I hope this helps, and definitely check out Formspree - it is a great service. If you want, you can stop reading now, in fact, I encourage it. What follows is 100% off topic for the rest of the post. Seriously - I don't mind if you stop.

Sad kitten is sad.

In a recent blog post, I got called out about the quality of code I used in an example. Many of the points made were absolutely true, but frankly, it was incredibly insulting and literally had me close to simply not blogging again. (To be clear, I'd still write, just not on my personal blog.) Because I feel it needs to be said, here are some things to keep in mind when reading this blog.

  • The code I write here is part of how I learn. That means, many times, you're seeing code from the beginning of my process of learning a particular language or technique. I think that has merit. I wish more beginners would share this part of the process. It helps flesh out issues with documentation and process that you don't get when only the experts are speaking.
  • When I'm explaining something, I absolutely do not go for the most tight, or short, code and will often break things that could be written on one line into many. I want my code to be readable, and approachable, and that is not always the same as production code.
  • Do I worry about misleading people? No. Frankly, there is a group of developers who cut and paste. You can call them StackOverflow developers if you want. You know them because as soon as they need to do something beyond the code they copied, they have no idea how to do it. I'm not going to belittle them because I've done it myself. Now - I think a good developer needs to recognize when they're doing that and take responsibility to actually learn what they are doing, but I flat out refuse to censor myself because someone may use my code incorrectly. Heck, I've been writing this blog for 15 years. I'm sure I've got some quite horrid little nasty gems in my past. Screw it. I'm proud of my mistakes.

So with that being said - as I said from the very beginning on this blog - I hope folks can learn from this blog (and laugh a bit) and if I've done that, then I'm not going to worry about writing the absolute best production quality minified work around.

Oh - but I will use semicolons. Because not using semicolons is like murdering kittens.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Robert Zehnder posted on 5/25/2016 at 7:44 PM

That being said, they probably should avoid my blog like the plague.

Comment 2 (In reply to #1) by Raymond Camden posted on 5/25/2016 at 7:45 PM

Heh!

Comment 3 by sydbarrett posted on 6/8/2016 at 3:16 PM

Hi, so I checked this, but I still don't get it. (yes I'm still crawling in coding). In the other form you add <input type="hidden" name="_next" value=""> so to forward the page to the "Thanks" page after sending. But here you didn't. So when you said: "You would modify the code to set the values to "" and not forward the page away."
I add the input hidden, with both value "" and value "thankyouBlock" but it still forwarded it to a new page. Can you help me out? Sorry if I'm pushing your good will

Comment 4 (In reply to #3) by Raymond Camden posted on 6/8/2016 at 3:50 PM

Ok so a few things. You do not want a form field called _next. That tells FormSpree where to go.

First - are you asking how to set values to ""? If so - I'd get a bit familiar with jQuery as this is basic usage type stuff. The general way is this:

$("#foo").val("");

where foo is the ID of a form field. You would do that one per field.

Next - I was a bit mistaken on the other blog post where I said my code 'forwarded' you on. I forgot my demo here simply hides content and shows content to thank the user.

Ok so far?

Comment 5 (In reply to #4) by sydbarrett posted on 6/8/2016 at 3:54 PM

Ok, so far so good.

Comment 6 (In reply to #5) by Raymond Camden posted on 6/8/2016 at 3:56 PM

Cool. That's pretty much it I think. Try it and see.

Comment 7 by Formspree posted on 6/12/2016 at 3:33 AM

Hi Raymond, cool post! It's gratifying to see people digging into our advanced features and helping others along! (Also, your jQuery looks good to me!)

One suggestion: You shouldn't need to include _replyto and email-- either one will set the reply-to email headers so you can reply with a click. (We should probably be more clear about that in our docs). Thanks again!

Comment 8 (In reply to #7) by Raymond Camden posted on 6/12/2016 at 12:38 PM

You are absolutely right, and frankly, the docs are pretty darn clear. I've updated the blog post with a note about my mistake. Thank you!

Comment 9 by James Joseph Finn posted on 6/22/2016 at 2:24 PM

Do you know how to get @Formspree to set the "Reply To Name" header? Setting the "reply to email" header is great, but it'd be nice to have a name also. Do you know how?

Comment 10 (In reply to #9) by James Joseph Finn posted on 6/22/2016 at 2:27 PM

Currently, for me, the:

name="name"

input value gets sent in the body of the formspree email, but I'd like to have this data sent in the header as well.

Comment 11 (In reply to #10) by Raymond Camden posted on 6/22/2016 at 2:33 PM

I think it is a good idea - but you would need to raise it to Formspree directly. I could see this being something they charge for though.

Comment 12 by Jens Geffken posted on 3/2/2017 at 6:15 PM

great post, exactly what i was looking for, thanks

Comment 13 by disqus_KeN7hC1ldz posted on 8/29/2017 at 1:10 PM

Fantastic post. I really understand what you're saying and have struggled with the best way to communicate an idea. Make no apologies, you're helping people and the critics aren't.

Comment 14 (In reply to #13) by Raymond Camden posted on 8/29/2017 at 1:12 PM

Thank you - I don't even remember the post before this that garnered the attack. I had another one last week, but in general, I'm average a "slam" about once a year, so that's probably pretty good. :)

Comment 15 by Dave Poole posted on 8/30/2017 at 2:44 PM

Just want to say thanks for the effort taken to capture the learning process you went through. The journey teaches more than the destination.

I don't know what trolls think they are achieving. Technology blogging is not a "only superheroes need apply" activity. Constructive criticism is helpful even if it stings a bit. The sting being proportional to the skill of the critic.

Comment 16 (In reply to #15) by Raymond Camden posted on 8/30/2017 at 3:11 PM

You are most welcome. :)

Comment 17 by Akash Sethi posted on 2/15/2018 at 11:47 PM

Fantastic post !
This code was working fine on localhost but after hosting my website i was not receiving emails from formspree.
I was facing this error because my emailaddress and new domain / page link was not verified with formspree.

Comment 18 (In reply to #17) by Raymond Camden posted on 2/16/2018 at 2:34 PM

So... you have it working ok?

Comment 19 by Tim Durrant posted on 5/5/2018 at 10:44 PM

Great stuff, Raymond. I needed a way to keep users on my site after a form submission, and you came through!

Comment 20 (In reply to #19) by Raymond Camden posted on 5/6/2018 at 5:36 PM

Glad it was helpful!

Comment 21 by NisGeek posted on 6/24/2018 at 8:58 PM

It's working on my localhost but not working from github. is there any way to fix it ?

Comment 22 (In reply to #21) by Raymond Camden posted on 6/25/2018 at 3:14 PM

How is it not working?

Comment 23 (In reply to #22) by NisGeek posted on 6/25/2018 at 8:15 PM

I don't know what happened,it is working now. Thanks for sharing the code with us. Really great stuff.

Comment 24 (In reply to #23) by Raymond Camden posted on 6/25/2018 at 8:19 PM

Cool.

Comment 25 by David Gordon posted on 8/4/2018 at 4:35 PM

Raymond, beautiful site, and exactly the article I was looking for. Do you think the fact that your email address ends up hard-coded in client-side script is a drawback of this solution? I would rather not give away the recipient of the form, but otherwise I think it's a great solution.

Comment 26 (In reply to #25) by Raymond Camden posted on 8/5/2018 at 6:51 PM

Um, it could be, but if it was a concern, I'd just use a non-primary email address. So for example, if I was some celebrity, I'd use a 'public facing' email address and not one my family and friends know.

You can do form processing with serverless easily enough and if you check my OpenWhisk and Webtask tag pages here, you will find an example of that.

Comment 27 (In reply to #26) by Raymond Camden posted on 8/5/2018 at 6:51 PM

I need to stop saying "easily enough" - it MAY be easy - check it out and let me know. :)

Comment 28 (In reply to #26) by David Gordon posted on 8/6/2018 at 7:40 PM

Yeah, agreed, a "public facing" account that just forwards to the actual recipient sounds like a decent way to go. I learned a long time ago that publishing your email address on a public site is asking for spam. But then again, maybe the bots are confused by the way that the email address is part of a URL in this case. Maybe that is good enough to break the address-scraping regex.