Multiple white staircases overlapping each other.
Photo by Liane Metzler on Unsplash

How to create a multi-step Hubspot form

There will be various use-cases for a Hubspot form with multiple steps for the user. If a form is particularly long, it will be easier to fill out if it's split into stages, with a clear indication of progress. It's also less intimidating to begin, and will entice more people to fill it out.

Regardless of the "why", there are two main methods to create a multi-step form for submission to Hubspot. They are to:

  • Create multiple forms that submit at each step - great for data collection along the journey, regardless of drop-off and "incomplete" forms
  • Create one single form, chopped up into sections with JS and using client-side validation on each step

I'll go over the pros and cons of each and a quick how-to...

Multiple submissions

This method is great for not losing data along the way. As the form is submitted on each step, Hubspot will create a contact with the first submission even if somebody drops out on the later stages.

This isn't so great if you want to create a form where "email address" isn't the first input (or at least on the first stage). It is possible to set your Hubspot profile to allow contacts to be made without email addresses, but for many this won't be ideal.

To make a multi-step form this way, you'll need to create a seperate form on HS for each stage. Richard Makara has made a lovely little multi-step form generator for Hubspot. This works really well, and is super simple and easy to set up. It utilises Hubspot's form creator too, so a lot of the hard work is done for you.

Single submission form

Cutting up a single form is a little trickier to do. You'll need to edit the form to create those steps yourself, add client-side validation on each step, as you pass through the form (unless you want to delve into the land of multi-step form errors...) and get comfortable with submitting data to a Hubspot form.

You'll need to create an "on submit" function on the form, to create a new XMLHttpRequest, process the form data, and finally send.

The URL needs to be in the following format:

var url = 'https://api.hsforms.com/submissions/v3/integration/submit/{portalId}/{formID}';

Next you'll need to compile the form data into the following format:

// Request JSON:
var data = {
  "fields": [
    {
      "name": "{fieldname}",
      "value": {fieldValue}
    },
    // for example:
    {
      "name": "firstname",
      "value": jQuery('input[name ="firstname"]').val()
    }
  ]
}

You can also add context and legal consent options to this data set like so:

"context": {
  "hutk": getCookie('hubspotutk'), // enable cookie tracking on submission
  "pageUri": window.location.href,
  "pageName": document.title
},
"legalConsentOptions":{ // when GDPR options are enabled
  "consent":{
    "consentToProcess":true,
    "text":"I agree to allow {company name} to store and process my personal data.",
    "communications":[
      {
        "value":true,
        "subscriptionTypeId":999,
        "text":"I agree to receive marketing communications from {company name}."
      }
    ]
  }
}

The hubspot cookie will be called "hubspotutk", so you can use the following function to grab that, and parse the name of the cookie as the parameter:

function getCookie(cname) {
  var name = cname + "=";
  var decodedCookie = decodeURIComponent(document.cookie);
  var ca = decodedCookie.split(';');
  for(var i = 0; i <ca.length; i++) {
      var c = ca[i];
      while (c.charAt(0) == ' ') {
      c = c.substring(1);
      }
      if (c.indexOf(name) == 0) {
      return c.substring(name.length, c.length);
      }
  }
  return "";
}

Read more about browser cookies on w3schools, including an explanation of the above function.

Once you have all your data, you'll need to stringify it, before completing the POST request.

var final_data = JSON.stringify(data);

xhr.open('POST', url);
xhr.setRequestHeader('Content-Type', 'application/json');
    
xhr.onreadystatechange = function() {
  if(xhr.readyState == 4 && xhr.status == 200) { 
      alert(xhr.responseText); // Returns a 200 response if the submission is successful.
  } else if (xhr.readyState == 4 && xhr.status == 400){ 
      alert(xhr.responseText); // Returns a 400 error the submission is rejected.          
  } else if (xhr.readyState == 4 && xhr.status == 403){ 
      alert(xhr.responseText); // Returns a 403 error if the portal isn't allowed to post submissions.           
  } else if (xhr.readyState == 4 && xhr.status == 404){ 
      alert(xhr.responseText); //Returns a 404 error if the formGuid isn't found     
  }
}
    
// Sends the request 
xhr.send(final_data)

Perfect. Now the form is submitted, you can do whatever you want with the thank you page/redirect etc.

The most crucial parts are obviously getting the portalId and formId correct in the url, and the format of the field data. Once everything is in order, it's actually super simple to edit, and the front-end can do whatever you want it to.

I massively recommend a progress indicator for any multi-step forms, and if you're struggling with clear, accessible and efficient yet beautiful form design, then look no further than the gov.uk design system. It's filled with fantastic designs for form components and simply some of the best error messaging design out there.

Happy Hubspotting!

A little digital blog, by a little analog person

© 2021 Ashley Chin