[How to] Run Custom Code/Scripts on Form Submission

Userlevel 7
  • Former Unbouncer
  • 126 replies

Ever needed a reliable way to fire some JavaScript on form submission?

This solution will be super handy for anybody looking to do things like: manipulating form data before it’s submitted or making custom API calls or XHR requests on form submission.

Whatever custom JavaScript you may have, using this script is a reliable and easy way to ensure it is fired on form submission.

Give’r a whirl! :spinbounce:

How to Install in Unbounce

Click Here for Instructions

This is not an official Unbounce feature. This functionality is based entirely on third party code, and has only been tested in limited applications. Since this isn’t a supported Unbounce feature, our support team will be unable to assist if things go awry. So if you are not comfortable with HTML, Javascript and CSS, please consider consulting an experienced developer.


There are two options for when your code will run:

  • beforeFormSubmit: Called after validation has passed, but before the lead has been recorded. This could be useful to manipulate the form data before it’s submitted.
  • afterFormSubmit: Called after the lead has been recorded. This could be useful to make an API call or show some kind of additional ‘thank you’ message to the lead.

Asynchronous hooks are supported: your function can return a promise, or it can take a callback as its second argument and call it when an asynchronous action has completed. This allows you to delay the next part of form submission until an asynchronous action (e.g. a fetch or XHR request) has completed. However, this is not generally recommended, because slowing down the form submit process could cause you to lose leads.

The functions will be called with an object as the first argument, containing the following properties:

  • formElement: The form’s DOM element
  • isConversionGoal: A boolean representing whether form submission was enabled as a conversion goal

Note: if you register multiple beforeFormSubmit and/or afterFormSubmit hooks, they will be called in parallel.


Here are some examples. You can use these as starting points to write your own hooks.

Example 1: update a form field value (trim spaces) before submit

  window.ub.hooks.beforeFormSubmit.push(function(args) {
    var input = args.formElement.querySelector('input#name');
    input.value = input.value.trim();

Example 2: show a message after submit

  window.ub.hooks.afterFormSubmit.push(function() {
    alert('Thank you!');

Example 3: asynchronously update a form field value from an external API before submit

Note: this example uses fetch which is not supported in Internet Explorer

  window.ub.hooks.beforeFormSubmit.push(function(args) {
    return fetch('https://reqres.in/api/users')
      .then(function(response) {
        return response.json();
      .then(function(body) {
        return body.data[0].first_name + ' ' + body.data[0].last_name;
      .catch(function() {
        return 'Unknown';
      .then(function(name) {
        args.formElement.querySelector('#name').value = name;

Can’t see the instructions? Log-in or Join the Community to get access immediately. 🚀

Want to take your Unbounce landing pages + Convertables™ to the next level?
:spinbounce: Check out the Ultimate List of Unbounce Tips, Scripts & Hacks

28 replies

Userlevel 7
Badge +1

Nice one, Noah! Excited to see how this may help conversion rates! 📈

Hey Noah,

Thanks for this script! This is going to be super helpful for an API call I have to make.

Thanks again!

I posted an update to github with some fixes. https://github.com/BillRothVMware/UnbounceJSSubmitCode

@Noah did anything changes recently? This code seems to have stopped working.

As I mentioned in the other post you replied to @BillRothVMware I am trying to convince support that this script is broken.
When I pointed this out the reply was:

This page was last saved and republished 2 days ago with this exact script in place as it was being tested specifically: https://www.random.gq/blank-page/ - the only script running on it is an alert - feel free to test it out. But as you can see when you do test it - the script is firing okay 🙂

I’ve created a page that shows the issues and explained the exact steps to replicate both issues with the script.
Issues being:

  • Payload doesn’t fire on a mobile device.
  • Script won’t obey a e.preventDefault call

Besides this script this also breaks both the multipage scripts on the forum as they both rely upon the e.preventDefault to stop the enter key from causing the entire form to validate. Therefore when a user presses enter the form will display all the validation issues on the entire form rather than just the specific page the user is looking at…

Just had a rather long but much appreciated email from support over the issues we having with this and other scripts. @Noah has just updated this script to make it more durable.

So I’ve stripped out a considerable about the the code from this newer script so it’s bare essentials. Mostly I’ve removed the aspects that should fire the validations/submissions that e.preventDefault is meant to be stopping.

With what is left in the following script all that should happen is the background colour should change… It shouldn’t validate and show errors. It shouldn’t submit the form on the button press.

It does fire the payload code but it’s still ignoring the e.preventDefault();

  <script type="text/javascript">
    function yourSubmitFunction(e, $) {
      try {
      catch(err) {
      finally {


    lp.jQuery(function($) {
      $('.lp-pom-form .lp-pom-button').unbind('click tap touchstart').bind('click.formSubmit tap.formSubmit touchstart.formSubmit', function(e) {
        yourSubmitFunction(e, $);
      $('form').unbind('keypress').bind('keypress.formSubmit', function(e) {
        if(e.which === 13 && e.target.nodeName.toLowerCase() !== 'textarea')
          yourSubmitFunction(e, $);

I submitted a feature request. Please comment/vote for it. This needs to be a supported feature.

In my case, this code appears to run inside an iframe of some sort (the confirmation dialog iframe?), so does not have access to the the JS variables initialized on the parent page (in my case the facebook tracking pixel). It was ALMOST exactly what I needed, but not quite.

This is awesome! Thank you so much @Noah

Is there a way to use it with embedded forms? We use embedded forms with our clients so that we don’t have access to their lead data:


Let me know! Any help would be greatly appreciated.

I am trying to run this with the Facebook lead pixel (tracking pixel AND a lead conversion pixel) however I’m following the instructions and using Facebook event testing the tracking pixel and lead pixel are firing upon page load. I have tried adding both the tracking and lead pixel, as well as just the lead pixel in the JS.

Does anyone have any suggestions? Thanks!

I am trying to achieve the same as Max (the comment above RE FB pixel) so if anyone has any suggestions, please let us know!


I want to make an API call in the beforeFormSubmit. API returns a boolean and if it is false, I would like to cancel the submit and keep the user on the form.

How do I do that? I did put a return false but that did not help.

You’ve managed to sort it out?

Would also like to do the same, keep the user on the form on an api error. Has anyone been able to work that out?

Any idea how to capture the form field variable to insert into javascript code in order to execute something with that value?

Given the form field’s ID in the HTML, you can capture the input by using


Note that this method captures the current input in the form field at the time it is called, so you’ll have to be sure to grab the value as you execute the function you want to operate with that information.

Thank you - that is what I needed!

has anyone manage to get this up?


Thanks for this useful script.

How can we declare this script for a pop-up within the parent page?

I need to execute parent javascript action after a pop-up submitted.

Thank you

Is there still no way of making a form wait to an API response?

I seem to be able to get it to work by using capture flag like so:

// Replace with the class you added to your form
const form = document.querySelector('.my-form');

form.addEventListener('submit', event => {
    // Once we verify everything, this global will be set to indicate everything is good and we can proceed
    // with Unbounce's handling of form submit in which case, we immediately return. Down below, we will
    // manually trigger form submit event once validation is successful.
    if (window.__VALIDATED) {

    // This will prevent Unbounce's default handlers from getting invoked which allows
    // you to process form submission.

    // ...Do API calls and whatnot here...

    // Once all checks are complete and if it's good shape:
    window.__VALIDATED = true;
    // Get reference to your submit button using jQuery or `document.querySelector` and manually trigger click

}, { capture: true }); // IMPORTANT!

Ideally, I wish Unbounce could give some public API similar to the hooks provided. The hooks aren’t very versatile, wish they could at least pass the event object so we don’t have to go for these hacks.


I’m trying to do a Marketo form submission in the background (per their own best practices) rather than the REST-based integration that Unbounce has and conditionally control the follow up behavior to either let Unbounce handle the redirect after submission, let the Marketo form default take over, or control it in my own JS through an onSuccess handler. I’ve successfully gotten Unbounce to wait for Marketo to finish processing by doing something like this:

	return new Promise(function(resolve){
	var mktoForm = MktoForms2.allForms()[0];
	mktoForm.addHiddenFields(getUBFormValues(ubForm)); //this is my own function that reads the UB form and passes to the hidden Marketo form

and my Marketo function:

	if(config.useUBTY){ //there's a config object to turn on/off using the UB thank you
		document.dispatchEvent(new CustomEvent("ub_mktoFormSubmitted",{bubbles: true});
		window.ub.form.url = config.tyLocation;
		document.dispatchEvent(new CustomEvent("ub_mktoFormSubmitted",{bubbles: true});
	return false;

If I don’t use the Promise, there’s a race condition that makes it ambiguous as to which follow up behavior executes, and implies that the Marketo form might not successfully submit.

This seems to work, but I’ve run into issues where my Lead info isn’t logged to Unbounce. I’m not sure if this is just that I’ve tested so many times Unbounce isn’t creating rows in the Lead table for me anymore or if there’s a wider issue at play.

This post should be pinned / highlighted / promoted / to save others time

I spent good day and a half bashing my head against the wall with capturing Unbounce form submission values into a data layer (and then sent as event props to our CDP).

beforeFormSubmit and afterFormSubmit hooks are the way to go here 👍

  window.ub.hooks.beforeFormSubmit.push(function(args) {
    var email = args.formElement.querySelector('input#email');

      'event': 'unbounce-form-submission',
      'form-email': email.value,
  // This is used as a trigger for another tag to do your work BEFORE the datalayer gets cleared 
  window.ub.hooks.afterFormSubmit.push(function() {
      'event': 'ub-form-success'

How does your dataLayer work? On every push makes an AJAX call? I also don’t see any waiting bits here so what happens if form submission navigation happens before your API gets called? Wouldn’t it cancel those requests?

I really wish the Unbounce team would detail the public APIs exposed in JS here https://developer.unbounce.com/ so that I could avoid writing messy code and not tap into private APIs that could potentially break in the future.

The tips & tricks stuff posted in these forums is nice, but it’s quite honestly a nightmare to use and maintain. I still see jQuery tips & tricks being posted here even though that’s a whole lot of extra KBs I need to ship to my users.

Please, could y’all just make this a tad-bit developer friendly?

My co-worker also figured the window.ub.form.url part that can let you change the form action URL without messing things up which is pretty neat. I’ve yet to see if this flow is good enough but I still worry if I’m tapping into a private API that Unbounce would change tomorrow and end up breaking the scripts.

This, combined with the async hooks could be the nail in the coffin for my woes but it’s quite horrible that these are all undocumented and you have to find it in the community forums.

@Noah or anyone else from the UB team, please pass this feedback and provide a good documentation for all the exposed global APIs we can use from client-side.