[How To] Add A Multi-Step Form 2.0

When you’re building your landing pages in Unbounce, you want the conversion process to be as simple as possible. Nothing too distracting, and you certainly don’t want to scare your audience away with long, time-consuming forms.

We’ve built an easy solution for you because, hey, we’re marketers too! :unbounce:

Introducing Multi-Step Forms! :chart_with_upwards_trend: :tada:

No more scary, conversion killing forms!
Follow the steps below to implement this handy script into your landing page and try it for yourself! And, as always, remember to A/B test, and keep us posted on your results!

You can see this in action (built in Unbounce) here:


How to Install in Unbounce

Click Here for Instructions

:rotating_light: WARNING: YOU ARE ENTERING DEVELOPER TERRITORY :rotating_light:
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.


Step 1.

Create your form in Unbounce.

Step 2.

Create a new button element for your ‘Next’ button and one for your ‘Previous’ button. Keep in mind when positioning these buttons (and your form submission button) that only one field will be shown at a time.

Step 3.

Copy this script and paste it into the Javascripts section of your page with placement ‘Before Body End Tag’:

<script type="text/javascript" src="https://rawgit.com/kimmobrunfeldt/progressbar.js/1.0.0/dist/progressbar.js"></script>

<script>
  (function() {
    // Update the following IDs with your own button IDs
    var backButton = document.getElementById('lp-pom-button-14');
    var nextButton = document.getElementById('lp-pom-button-13');
    var showProgressBar = false;

    var submitButton = document.querySelector('.lp-pom-form .lp-pom-button');
    var formContainer = document.querySelector('.lp-element .lp-pom-form');
    var currentField = 0;
    var allFields = document.getElementsByClassName('lp-pom-form-field');
    var errorSpan = document.createElement('span');

    for (i = 0; i < allFields.length; i++) {
      allFields[i].classList.add('hide');
      allFields[i].style.top = '0px';
    }

    backButton.classList.add('hide');
    submitButton.classList.add('hide');

    allFields[0].classList.remove('hide');

    if (allFields[0].querySelector('input')) {
      allFields[0].querySelector('input').focus();
    }

    errorSpan.classList.add('hide');
    errorSpan.style.color = 'red';
    errorSpan.style.position = 'absolute';
    var labelHeight;
    if (allFields[0].querySelector('label')) {
      labelHeight = allFields[0].querySelector('label').clientHeight;
    } else {
      labelHeight = 25;
    }
    errorSpan.style.top = '-' + labelHeight + 'px';
    formContainer.appendChild(errorSpan);

    if (showProgressBar) {
      var progressContainer = document.createElement('div');
      progressContainer.id = 'container';
      formContainer.appendChild(progressContainer);

      var bar = new ProgressBar.Line(container, {
        strokeWidth: 4,
        easing: 'easeInOut',
        duration: 1400,
        color: '#FFEA82',
        trailColor: '#eee',
        trailWidth: 1,
        svgStyle: { width: '100%', height: '100%' },
        from: { color: '#FFEA82' },
        to: { color: '#74D16A' },
        text: {
          style: {
            // Text color.
            // Default: same as stroke color (options.color)
            color: '#fff',
            position: 'absolute',
            right: '0',
            top: '30px',
            padding: 0,
            margin: 0,
            transform: null,
          },
          autoStyleContainer: false,
        },
        step: function(state, bar) {
          bar.setText(currentField + 1 + ' of ' + allFields.length);
          bar.path.setAttribute('stroke', state.color);
        },
      });
    }

    function nextEvent() {
      allFields[currentField].classList.add('hide');

      currentField += 1;
      allFields[currentField].classList.remove('hide');
      if (allFields[currentField].querySelector('input')) {
        allFields[currentField].querySelector('input').focus();
      }

      if (currentField > 0) {
        backButton.classList.remove('hide');
      }

      if (currentField === allFields.length - 1) {
        submitButton.classList.remove('hide');
        nextButton.classList.add('hide');
      }

      updateProgress();
    }

    function backEvent() {
      allFields[currentField].classList.add('hide');
      submitButton.classList.add('hide');

      currentField -= 1;
      allFields[currentField].classList.remove('hide');
      if (allFields[currentField].querySelector('input')) {
        allFields[currentField].querySelector('input').focus();
      }
      if (currentField === 0) {
        backButton.classList.add('hide');
        nextButton.classList.remove('hide');
      }
      if (currentField <= allFields.length - 1) {
        nextButton.classList.remove('hide');
      }

      updateProgress();
    }

    function currentFieldInvalid() {
      var invalidInput = allFields[currentField].querySelector(':invalid');

      if (invalidInput) {
        errorSpan.textContent = invalidInput.validationMessage;
      }

      return Boolean(invalidInput);
    }

    function updateProgress() {
      if (showProgressBar) {
        var barSize = (currentField + 1) / allFields.length;
        bar.animate(barSize); // Number from 0.0 to 1.0
      }
    }

    updateProgress();

    nextButton.addEventListener('click', function(e) {
      if (currentFieldInvalid()) {
        if (allFields[currentField].querySelector('input')) {
          allFields[currentField].querySelector('input').classList.add('hasError');
        } else {
          allFields[currentField].children[1].classList.add('hasError');
        }

        errorSpan.classList.remove('hide');
        e.preventDefault();
      } else {
        if (allFields[currentField].querySelector('input')) {
          allFields[currentField].querySelector('input').classList.remove('hasError');
        } else {
          allFields[currentField].children[1].classList.remove('hasError');
        }

        errorSpan.classList.add('hide');
        nextEvent();
      }
    });

    backButton.addEventListener('click', backEvent);

    submitButton.addEventListener('click', function(e) {
      if (currentFieldInvalid()) {
        if (allFields[currentField].querySelector('input')) {
          allFields[currentField].querySelector('input').classList.add('hasError');
        } else {
          allFields[currentField].children[1].classList.add('hasError');
        }

        errorSpan.classList.remove('hide');
        e.preventDefault();
      } else {
        if (allFields[currentField].querySelector('input')) {
          allFields[currentField].querySelector('input').classList.remove('hasError');
        } else {
          allFields[currentField].children[1].classList.remove('hasError');
        }

        errorSpan.classList.add('hide');
      }
    });

    document.body.addEventListener('keydown', function(e) {
      var keyCode = e.keyCode || e.which;

      // Enter key
      if (keyCode === 13 && currentField < allFields.length - 1) {
        if (currentFieldInvalid()) {
          if (allFields[currentField].querySelector('input')) {
            allFields[currentField].querySelector('input').classList.add('hasError');
          } else {
            allFields[currentField].children[1].classList.add('hasError');
          }

          errorSpan.classList.remove('hide');
          e.preventDefault();
        } else {
          if (allFields[currentField].querySelector('input')) {
            allFields[currentField].querySelector('input').classList.remove('hasError');
          } else {
            allFields[currentField].children[1].classList.remove('hasError');
          }

          errorSpan.classList.add('hide');
          e.preventDefault();
          nextEvent();
        }
      } else if (keyCode === 13 && currentField === allFields.length - 1) {
        if (currentFieldInvalid()) {
          if (allFields[currentField].querySelector('input')) {
            allFields[currentField].querySelector('input').classList.add('hasError');
          } else {
            allFields[currentField].children[1].classList.add('hasError');
          }

          errorSpan.classList.remove('hide');
          e.preventDefault();
        } else {
          if (allFields[currentField].querySelector('input')) {
            allFields[currentField].querySelector('input').classList.remove('hasError');
          } else {
            allFields[currentField].children[1].classList.remove('hasError');
          }

          errorSpan.classList.add('hide');
        }
      }

      if (keyCode === 37) {
        // Left key
        if (currentField > 0) {
          backEvent();
        }
      } else if (keyCode === 39) {
        // Right key
        if (currentField < allFields.length - 1) {
          if (currentFieldInvalid()) {
            if (allFields[currentField].querySelector('input')) {
              allFields[currentField].querySelector('input').classList.add('hasError');
            } else {
              allFields[currentField].children[1].classList.add('hasError');
            }

            errorSpan.classList.remove('hide');
            e.preventDefault();
          } else {
            if (allFields[currentField].querySelector('input')) {
              allFields[currentField].querySelector('input').classList.remove('hasError');
            } else {
              allFields[currentField].children[1].classList.remove('hasError');
            }

            errorSpan.classList.add('hide');
            nextEvent();
          }
        }
      }
    });
  })();

  /**
   * Do not remove this section; it allows our team to troubleshoot and track feature adoption.
   * TS:0002-03-062
   */
</script>

Step 4.

Update the script with the ID of your ‘Previous’ and ‘Next’ button elements. Tip: Make sure you exclude the ‘#’ in the ID.

Step 5.

Copy this script and paste it into the Stylesheets section of your page:

<style>
  #container {
    margin-top: -45px;
    width: 100%;
    height: 8px;
    position: relative;
  }
  .lp-form-errors {
    display: none !important;
  }
  .hasError {
    border: 1px solid tomato !important;
    box-shadow: 0px 0px 5px 1px tomato !important;
  }
  .hide {
    display: none !important;
  }
  .lp-pom-form-field,
  .lp-pom-button {
    -webkit-animation: KEYFRAME-NAME 2s;
    -moz-animation: KEYFRAME-NAME 2s;
    -o-animation: KEYFRAME-NAME 2s;
    animation: KEYFRAME-NAME 2s;
  }

  @-webkit-keyframes KEYFRAME-NAME {
    0% {
      opacity: 0;
    }
    100% {
      opacity: 1;
    }
  }
  @-moz-keyframes KEYFRAME-NAME {
    0% {
      opacity: 0;
    }
    100% {
      opacity: 1;
    }
  }
  @-o-keyframes KEYFRAME-NAME {
    0% {
      opacity: 0;
    }
    100% {
      opacity: 1;
    }
  }
  @keyframes KEYFRAME-NAME {
    0% {
      opacity: 0;
    }
    100% {
      opacity: 1;
    }
  }
</style>

You’re done!

Now sit back and let the conversions roll in… :gif_master:


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


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

25 Likes

What a beaut! :noah:

2 Likes

@Noah, you beautiful wizard you. You’ve done it again. :raised_hands: :noah:

1 Like

I suddenly wish I’d learn some code instead and spoil the entire community here, the way @Noah does :slight_smile:

3 Likes

Brilliant :slight_smile:

Had sent briefs to developers to do this - now no need - thanks very much :slight_smile: :slight_smile: :slight_smile:

2 Likes

I use a lot of multi step forms - the old fashion way with 2 different pages.
Yes I know - thats a rather complicated way to do the setup - however I really like that I have the data from step 1 even if the user wont fill out step 2.

Is this the case here as well?

4 Likes

This! I would love to know as well.

Meanwhile, @Noah - thanks so much!

@Mark_H + @inboundbear this solution won’t record data after each step–it simply splits a single form into multiple steps and submits the data at the end.

2 Likes

This is fantastic. We have spent countless hours looking for a multi step form for Unbounce. We have tested Typeform and are currently testing an app called Leadformly which you can embed into unbounce pages. Of course you have to Zap your leads somewhere and you don’t get to use Unbounce A/B testing etc. so it’s not great.

Where is your native multi step form builder Unbounce?

3 Likes

Hey Ben!

Didn’t want to leave your question hanging here, especially because I’m sure there are some other folks wondering the same thing.

To be completely transparent - the code for our forms is some of the oldest code that exists in our entire product. Tampering with them is an enormous undertaking, and at this point in time our focus has been on different improvements (Convertables, Bulk Actions, etc.). It’s for this reason that we share these types of Tips & Scripts to give you all some options in the meantime before we start updating our native features.

Hopefully this provides a little bit of insight into this situation for now. Please keep the feedback and questions coming! And I’m glad to hear you’re liking this workaround so far. :slight_smile:

Happy Friday! :beers:

2 Likes

Hey Jess
Thanks for the info. I have confidence that you’ll get to it. The desire for conversions will lead you there ;–)
Ben

1 Like

Before I put this in to practice I just want to confirm if this will work for multi-field forms as well. I am building a referral page and want to use step one to collect the current customer into and step two to collect the prospect info…will this work for this?

1 Like

I have a question. I use multi step forms all the time in unbounce but each page is separate. I am looking at this script and the instructions and curious if I am able to do the following:

1st step is a question using radio button
2nd step would be a question using check boxes
3rd step is another radio button
4th, 5th, 6th steps would be name, email, phone number information.

Is this possible or am i only able to use name, phone, email address text fields?

Thank you.

Noah - this works great, thanks man! I would like to know if users are completing some of the questions and abandoning the form. With the old multi-page method we could see conversions on each page/step of the form.

Can you think of any way, either directly in Unbounce or using a 3rd party tracker like Hotjar, that I could see how many users are starting the form and abandoning?

@Noah didn’t tag you before, tagging you now so you see this. Thanks.

Hmmmm, this is a good question @joshlevitan. Just curious, what did you find overall? Did you find any notable drop-offs? Would love to hear how measuring that impacted how you build your landing pages.

@Justin With the multi-page forms we found that half the people dropped off after the on-page part of the form, and about half of the remaining visitors finished the form. We redesigned the whole page, and as part of that redesign we switched to this dynamic form.

We don’t know for sure if people have dropped off on this form. We’re trying to track using taps/clicks from Hotjar but not sure if it works well with the dynamic page. We’re getting very low conversions (under 5%) so we’re just trying to understand what users are doing on our page, if anything. Perhaps knowing if they start but then don’t finish the form would suggest we ask too many questions or one specific question is too hard to answer.

Hey Josh we have found Hotjar’s recording feature very useful for working out what users are doing on the page, including with the form itself.
No doubt you are already looking at that.

1 Like

Hello,

This is a great script. I implemented at http://ar-vida.primerolafamilia.com.ar/seguro_vida1/

I have a problem I can’t seem to solve. When my landing page loads, instead of loading at the top (header), it loads at my form or section where form is. If I disable the form, it will load like any regular page at the top.

This happens after I implemented this script.

Could someone please help me?.

Thanks!

Max

hey @MaxG_BA, I think this is being caused by the auto-focus set up with this script.

To get rid of this, you can simply remove lines 26-28 of the script seen here: https://www.screencast.com/t/hZKYx2scOn2

Hope this helps!

5 Likes