How to create multi-step product options in WooCommerce

If you have a complex WooCommerce product with many different options, it may be a good idea to split those options in a multi-step form. This tutorial will show how to achieve multi-step product options in your WooCommerce store.

What we’re building

We’ll create product options, split into multiple steps. A progress bar indicates how far the user is in the process before being able to add the product to their cart.

What you’ll need

  • A store with WooCommerce (3.4.0 or higher) installed and configured. We assume this is already done on your end but just in case it’s not, you can read this article to get started with WooCommerce.
  • Our Advanced Product Fields for WooCommerce plugin. The plugin has a free version, but for this tutorial, you’ll need the premium version. This plugin is capable of adding options to your product.

Let’s get started!

1. Configure the options on your product

If you have our plugin up & running, edit your WooCommerce product and find the Custom fields section under Product Data:

custom fields tab in woocommerce product

Here, you can add all options that belong to your product. The important part is to wrap your options in different sections. Each section will represent one step on your product page.

You can add sections by clicking New Field and then choosing Section and Section End as field types. Adding fields to a section is as simple as dragging them between the Section and Section End fields.

Make sure to add a class name to each section, called “step”. You can do that in the section field settings:

Below is an example of 2 sections each containing a few options:

In a later step, we’ll transform these sections into steps with CSS.

2. Add progress bar & buttons

Next, we need to add some HTML to display a progress bar and buttons to navigate between the steps. That’s done by simply copy/pasting the code below into your active (child) theme’s functions.php file:

add_action('wapf_before_wrapper', 'wapf_before_wrapper');
function wapf_before_wrapper($product) {
	?>
	<div class="wapf-progress">
		<div class="wapf-progress-bar"></div>
		<div class="wapf-progress-steps"></div>
	</div>
	<?php
}

add_action('wapf_before_product_totals', 'wapf_before_product_totals');
function wapf_before_product_totals($product){
	?>
	<div class="wapf_step_buttons">
		<button class="button wapf_btn_prev" style="display:none">Previous</button>
		<button class="button wapf_btn_next">Next</button>
	</div>
	<?php
}

Note this code will add a progress bar & buttons to all your products. If you’d like to limit the products that require a multi-step form, you can use this code instead:

add_action('wapf_before_wrapper', 'wapf_before_wrapper');
function wapf_before_wrapper($product) {
        $product_ids = array(123,987,630);
        if(!in_array($product->get_id(),$product_ids)) return;
	?>
	<div class="wapf-progress">
		<div class="wapf-progress-bar"></div>
		<div class="wapf-progress-steps"></div>
	</div>
	<?php
}

add_action('wapf_before_product_totals', 'wapf_before_product_totals');
function wapf_before_product_totals($product) {
        $product_ids = array(123,987,630);
        if(!in_array($product->get_id(),$product_ids)) return;
	?>
	<div class="wapf_step_buttons">
		<button class="button wapf_btn_prev" style="display:none">Previous</button>
		<button class="button wapf_btn_next">Next</button>
	</div>
	<?php
}

In the code above, note the two lines $product_ids = array(123,987,630);. You should edit those lines to include the product ID’s you want the multi-step form working on.

3. Add styling

In this step, we’ll add CSS to style the progress bar, buttons, and sections. Add the CSS below to your theme via Appearance > Customize > Additional CSS.

Please note the CSS may be a little different for your WordPress theme, but it should be pretty close. I’ve added some comments in bold that may be useful to look at.

div.quantity, .woocommerce .button[name=add-to-cart] {
    display:none; /*Hide Add to Cart button until last step*/
}
.wapf-wrapper{
    border-radius: 4px;
    border: 1px solid #ededed;
    padding: 15px 20px;
    margin-bottom:20px;
}
.wapf-field-group .step{
    display:none;
}
.wapf-field-group .step:first-child{
    display:flex;
}
.wapf_step_buttons{
    margin-bottom:20px;
    overflow:hidden;
}
.wapf_btn_next{
    float:right !important;
}
.wapf-progress{
    position:relative;
    max-width:450px;
    margin: 0 auto;
}
.wapf-progress:before, .wapf-progress-bar{
    content:'';
    position:absolute;
    height:3px;
    width:100%;
    background:#ededed;
    top:14px;
    left:0;
}
.wapf-progress-steps{
    margin-bottom: 30px;
    overflow: hidden;
    counter-reset: step;
    display: flex;
    justify-content: space-between;
}
.wapf-progress-steps div{
    position:relative;
}
.wapf-progress-steps div:before{
    content: counter(step);
    counter-increment: step;
    width: 30px;
    height:30px;
    font-size:16px;
    line-height:30px;
    border-radius:50%;
    text-align:center;
    display: block;
    font-size: 10px;
    background: #ededed;
}
.wapf-progress-steps div.active:before{
    background:#f0632b; /*The finished step color. Feel free to change*/
    color:white;
}

.wapf-progress-bar{
    background:#f0632b; /*The finished step color. Feel free to change*/
    width:0%;
}

Note this CSS will change the appearance for all product pages. If you want to limit the products that have a multi-step form, some of the CSS should be changed to only affect those products. This can easily be done if you know your product ID.

The below code only adds multi-step styling to a product with ID 123. Note that you only need to change the first two styling blocks (as shown in the example below). All other CSS from the code above remains the same:

.post-123 div.quantity, .post-123 .woocommerce .button[name=add-to-cart] {
    display:none;
}
.post-123 .wapf-wrapper{
    border-radius: 4px;
    border: 1px solid #ededed;
    padding: 15px 20px;
    margin-bottom:20px;
}

As you can see, the CSS above contains an extra portion to every rule: .post-123 where 123 is the ID of your product. If you need it for more than 1 product, you can do it like this:

.post-123 .wapf-wrapper,.post-345 .wapf-wrapper, .post-678 .wapf-wrapper{ ... }

Make things dynamic

By now, you’ll see the multi-step form is displayed nicely on your Woo product page. But it needs some Javascript logic to make things work. Add the following code in your (child) theme’s functions.php file:

add_action('wp_footer','wapf_steps_script');
function wapf_steps_script(){
	?>
	<script>
	jQuery(document).ready(function() {
		
		var steps = jQuery('.wapf-section.step');
                if(!steps.length) return;
		var maxSteps = steps.length;
		var $prev = jQuery('.wapf_btn_prev');
		var $next = jQuery('.wapf_btn_next');
		var $stepList = jQuery('.wapf-progress-steps');
		var $bar = jQuery('.wapf-progress-bar');
		var $cart = jQuery('div.quantity,[name="add-to-cart"]');
		var currentStep = 1;
		var $form = jQuery('form.cart');
		
		for(var i = 1; i <= maxSteps;i++) {
			var $div = jQuery('<div>');
			if(i === 1)
				$div.addClass('active');
			$stepList.append($div);
		}
								
		var post = function(e) {
			var max = $stepList.find('div:visible').length;

			e.preventDefault();

			steps.hide();
			steps.eq(currentStep-1).show();
			
			$stepList.find('div').removeClass('active').eq(currentStep).prevAll().addClass('active');
			if(currentStep == max) {
				$stepList.find('div').addClass('active');
				$cart.show();
			} else {
				$cart.hide();
			}
			
			$bar.css('width', (currentStep-1)*(100/(max-1))+'%');
			
			$prev.hide();
			$next.hide();
			if(currentStep < max)
				$next.show();
			if(currentStep > 1)
				$prev.show();
		}
		
		var isValid = function() {
			
			var $current = steps.eq(currentStep-1);
			var $inputs = $current.find(':input');
			
			for(var i = 0;i<$inputs.length;i++) {
				if(!$inputs[i].checkValidity())
					return false;
			}
			
			return true;
		}
		
		$prev.on('click', function(e) {
			currentStep--;
			post(e);
		});
		
		$next.on('click', function(e) {
			var $current = steps.eq(currentStep-1);
			var valid = isValid();
			if(isValid()) {
				currentStep++;
				post(e);
			}
		});
		
		jQuery(document).on('wapf/dependencies', function(){
			$stepList.find('div').removeClass('wapf-hide');
			steps.each(function(i,s){
				var $s = jQuery(s);
				if($s.hasClass('wapf-hide'))
					$stepList.find('div:eq('+i+')').addClass('wapf-hide');
			});
		});
		
	});
	</script>
	<?php
}

The script will make sure the progress bar is updated correctly, the "previous" and "next" buttons do their work, and the correct section is shown at any time.

That's it! Now you should have a working multi-step product options form!