WooCommerce: Limit Shipping to Only One State

Today’s snippet has been widely requested by many readers, clients and WooCommerce fans. We already saw in the past how to Limit State Dropdowns to One State Only (for both Shipping & Billing) and How to Sell to one State only (Billing).

However, we never covered a much more common setting: what happens when Billing is allowed to every state but Shipping is limited?

In order to get a little help, I’ve reached out to Diego Zanella, a WooCommerce genius who is also the author of the Aelia Currency Switcher plugin for WooCommerce.

PHP Snippet (1 of 2): Allow Shipping to Only One State @ WooCommerce Cart Shipping Calculator

/**
 * @snippet       Ship to One State @ WooCommerce Cart Shipping Calculator
 * @how-to        Get CustomizeWoo.com FREE
 * @author        Diego Zanella
 * @compatible    WooCommerce 3.6.4
 * @community     https://businessbloomer.com/club/
 */
 
add_filter( 'woocommerce_states', 'aelia_cart_filter_US_states' );
 
function aelia_cart_filter_US_states( $states ) {
if ( is_cart() ) {
   $states['US'] = array(
      'PA' => 'Pennsylvania',
   );
}
return $states;
}
WooCommerce: Allow Shipping to Only One State in the Cart Shipping Calculator
WooCommerce: Allow Shipping to Only One State in the Cart Shipping Calculator

PHP Snippet (2 of 2): Allow Shipping to Only One State @ WooCommerce Checkout Page

Note: this will only work if you’ve limited shipping to a specific country via the WooCommerce settings. Also, this snippet might limit billing too – in this case check the alternative snippet below.

/**
 * @snippet       Ship to One State @ WooCommerce Checkout #1
 * @how-to        Get CustomizeWoo.com FREE
 * @author        Rodolfo Melogli
 * @compatible    WooCommerce 3.6.4
 * @community     https://businessbloomer.com/club/
 */

add_filter( 'woocommerce_countries_shipping_country_states', 'bbloomer_set_checkout_shipping_state' );
  
function bbloomer_set_checkout_shipping_state( $states ) {
	$states[ 'US' ] = array( 'PA' => __( 'Pennsylvania', 'woocommerce' ) );
  	return $states;
}

Alternative snippet – in case everything else fails:

/**
 * @snippet       Ship to One State @ WooCommerce Checkout #2
 * @how-to        Get CustomizeWoo.com FREE
 * @author        Rodolfo Melogli
 * @compatible    WooCommerce 3.6.4
 * @community     https://businessbloomer.com/club/
 */

add_action( 'wp_footer', 'aelia_checkout_shipping_filter_US_states' );
  
function aelia_checkout_shipping_filter_US_states() {
	if ( ! is_checkout() ) {
		return;
	}
	?>
  	
	<script>
	jQuery(document).ready(function($) {

		$(document.body).on('country_to_state_changed', function() {

			function set_shipping_state(state) {
				var $shipping_state = $('#shipping_state');
				var $shipping_state_option = $('#shipping_state option[value="' + state + '"]');
				var $shipping_state_option_no = $('#shipping_state option[value!="' + state + '"]');
				$shipping_state_option_no.remove();
				$shipping_state_option.attr('selected', true);
			}

			var $shipping_country = $('#shipping_country');

			var new_shipping_state = '';

			switch($shipping_country.val()) {
				case 'US':
					new_shipping_state = 'PA';
					break;
			}

			if( ! $.isEmptyObject(new_shipping_state)) {
				set_shipping_state(new_shipping_state);
			} 

		});

	});  
	</script>
		
	<?php
}
WooCommerce: Allow Shipping to Only One State @ Checkout
WooCommerce: Allow Shipping to Only One State @ Checkout

Where to add custom code?

You should place custom PHP in functions.php and custom CSS in style.css of your child theme: where to place WooCommerce customization?

This code still works, unless you report otherwise. To exclude conflicts, temporarily switch to the Storefront theme, disable all plugins except WooCommerce, and test the snippet again: WooCommerce troubleshooting 101

Related content

  • WooCommerce: “You Only Need $$$ to Get Free Shipping!” @ Cart
    This is a very cool snippet that many of you should use to increase your average order value. Ecommerce customers who are near the “free shipping” threshold will try to add more products to the cart in order to qualify for free shipping. It’s pure psychology. Here’s how we show a simple message on the […]
  • WooCommerce: Weight-Based Shipping Methods
    With WooCommerce you get 3 default shipping methods: Flat Rate, Free Shipping, Local Pickup. For each one you can define a cost, however there is no way to set up some “weight” restrictions. So, what if you want to display a rate for orders below 10 kg, and another shipping rate for orders above that […]
  • WooCommerce: Hide Shipping Method If Shipping Class Is In The Cart
    Our goal is to check if a Product with a specific Shipping Class is in the Cart, and consequently disabling a shipping rate such as Free Shipping if this is true. This is super useful when there are multiple items in the cart and you don’t want to give free shipping for certain orders for […]
  • WooCommerce: Hide Shipping Rates if Free Shipping Available
    If Free Shipping is available, you possibly don’t want to show the other premium shipping options. WooCommerce shows by default all shipping rates that match a given shipping zone, so it’s not possible to achieve this from the settings alone. Thankfully, the “woocommerce_package_rates” filter allows us to manipulate the shipping rates before they are returned […]
  • WooCommerce: Hide Shipping If Local Pickup Is Selected
    Let’s talk about checkout UX: if a user is willing to pick up the item in store, why should there be a shipping form on the checkout? Well, let’s see how we can hide this dynamically with a bit of PHP and JS!

Diego Zanella

Owner at Aelia.co, I founded the business in January 2013 to fill a gap in the WooCommerce market after designing and developing, from the ground up, the Currency Switcher for WooCommerce, an innovative plugin that quickly became the leading multi-currency solution for WooCommerce.

Rodolfo Melogli

Business Bloomer Founder

Author, WooCommerce expert and WordCamp speaker, Rodolfo has worked as an independent WooCommerce freelancer since 2011. His goal is to help entrepreneurs and developers overcome their WooCommerce nightmares. Rodolfo loves travelling, chasing tennis & soccer balls and, of course, wood fired oven pizza. Follow @rmelogli

45 thoughts on “WooCommerce: Limit Shipping to Only One State

  1. Is there a way to use the JavaScrip but with multiple states? I can’t figure that out!

    1. Hi Gracen, thanks so much for your comment! Yes, this is definitely possible, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

  2. Now, I find out another simple workaround:
    First, I’ve created a brand new select field with all the states i needed to show. In my country, Chile, they are “comunas”. Like this:

    add_filter('woocommerce_shipping_fields' , 'my_new_field', 9999);
    function my_new_field( $fields )
    {
        $fields['shipping_comuna']['type'] = 'select';
        $fields['shipping_comuna']['options'] = shipping_zone_data()['comunas']; // custom function returns array of states listed on all shipping methods
        $fields['shipping_comuna']['class'] = array('form-row-last', 'update_totals_on_change ');  //updates the totals!
        $fields['shipping_comuna']['required'] = true;
        $fields['shipping_comuna']['label'] = 'Comuna';
        $fields['shipping_comuna']['placeholder'] = __('Selecione comuna de despacho', 'my_theme_slug');
       return $fields;
    }
    

    Then, I’ve linked the selected value in this field to shipping_state official field, like this:

    add_action('woocommerce_before_checkout_form','link_state_comuna');
    function link_state_comuna()
    {
    	?>
    	<script type="text/javascript">
    		jQuery(document).ready(function($){
    			$('#shipping_comuna').change(function(){
    				$('#shipping_state').val($(this).val());
    			});
    		});
    	</script>
    	<?php
    }
    

    Finally I hide the official shipping state field with CSS:
    #shipping_state {display:none;}
    et voila

    1. Nice!

  3. God, this was a nightmare!
    Hi Rodolfo, as always very useful tip. But i tried both php snippet and the js version. The php changed my billing state field and using the later the selection is lost for shipping charge as other people comment. Then i tried the Dan version but nothing changes.
    After hours i figured out a typo in Travis code:
    $shipping_state.append(” + states[state] + ”);
    should be
    $shipping_state.append(” + states[state] + ”);
    ΒΏHow is this possible?
    Bye

    1. part of the code transcription was missing in my comment. The thing was a missing “

      1. Ok thank you!

  4. Thanks for all the snippets you provide! I had the need to prevent shipping to specific states in MΓ©xico temporarily due to some COVID-19 reasons, so I came across this snippet and solve it in a different way.
    I created a Shipping Zone in WooCommerce settings and choose the States I DO NOT WANT to ship to in the Region field.
    Then I left the shipping methods empty. Not adding anything.
    I also moved this new shipping zone to the top of my shipping zones.
    This will show a message to the user if they choose the state I do not ship to that says something like: “No shipping is available. Please….” and won’t allow the user to pay unless the Shipping State is not the ones from my list.
    I also added a message at the top of the checkout page to communicate to the user that we are not currently shipping to those states to make it clear they have a correct address but we temporarily do not ship to that region.

    1. Nice!

      1. Hello there. I have tried using the PHP snippet (2 of 2) which it works properly, however it affects the billing form as well.
        The other snippets do not work on woocommerce 4.2.0.
        The snippet I posted on 2016 does not work any longer as well.

        Can anyone assist? I would like the billing form not to be affected. I would really appreciate.

        1. Tried the alternative jQuery snippet?

          1. I did and it does not work on latest version of woocommerce.

            1. Actually, it does work but I cannot figure out how to add an array of multiple states in the
              ( new_shipping_state = ) line

              1. Ah ok

  5. I used the code at the link below and it worked fine. It seems much simpler, so I’m wondering if it’s doing the same thing.
    https://www.ultimatewoo.com/restrict-sales-specific-u-s-states-woocommerce/

    1. Hey Jodi, it’s much simpler because it completely remove those states from everywhere: yes – shipping – but also billing, dropdowns, reports, and everywhere a state selection is needed. My snippet is just for hiding them from the shipping form @ checkout, so that e.g. someone can buy from Hawaii but cannot ship to Hawaii

  6. Hey, I added this code to my client’s website back in May 29 and just recently my client noticed some orders made from the front-end do not have taxes applied. After some testing I found this code was the cause. I have since switched my client’s WooCommerce setting to calculate tax based on their store address for now since they only ship within their province.

    I want to ask has anyone encountered this particular issue with this code? And if there are other alternatives to limiting shipping checkout to one state/province? Any insight would be helpful, thank you!

    1. Hey Wallace, thanks so much for your comment! Yes, this is definitely fixable, but I’m afraid it’s custom work. If you’d like to get a quote, feel free to contact me here. Thanks a lot for your understanding!

  7. Hi, any updates on this, I could not get it to work with current woocommerce version.

    1. Hi there, just tested and it still works πŸ™‚

  8. Hi,
    First of all thank you for your help. I have found an error in your script, there is missing double quotes in state id attribute.

    1. Thank you Sandeep! Is the error here:

      <option id="' + state + ' value="' + state + '">
      
  9. I came up with a slightly different solution based on the work done here. It utilizes wp_localize_script to be able to populate the states from a PHP function. In this case, ssc_ship_states returns an array of states for multiple countries (Australia, US, Canada), so all three of those countries will have a restricted list of states/provinces.

    function ssc_checkout_shipping_filter_US_states() {
      if(!is_checkout()) {
        return;
      }
    	global $SSC_ASSET_PATH;
    	
    	wp_enqueue_script( 'checkout-states', $SSC_ASSET_PATH . 'js/checkout-states.js', array('jquery'), null, true );
    	wp_localize_script( 'checkout-states', 'state_data', array(
    		'ship_states' => ssc_ship_states()
    	));
    };
    add_action('wp_enqueue_scripts', 'ssc_checkout_shipping_filter_US_states', 100);
    

    Then I use a separate JS file that gets enqueued in the footer:

    jQuery(document).ready(function($) {
    	$(document.body).on('country_to_state_changed', function(event, args) {
    		function set_shipping_states(states) {
    			var $shipping_state = $('#shipping_state');
    			$shipping_state.find('option:not([value=""])').remove();
    			
    			for(state in states) {
    				$shipping_state.append('<option id="' + state + ' value="' + state + '">' + states[state] + '</option>');
    			}
    		}
    		
    		var $shipping_country = $('#shipping_country');
    		
    		var new_shipping_states = {};
    		var country = $shipping_country.val();
    		if (state_data.ship_states[country]) {
    			new_shipping_states = state_data.ship_states[country];
    		}
    		
    		if(!$.isEmptyObject(new_shipping_states)) {
    			set_shipping_states(new_shipping_states);
    		}
    	});
    });
    

    It should be noted that I also have removed Select2 scripts and styles from Woo because they seem pointless to me and cause more headache than they are worth:

    function bones_dequeue_stylesandscripts_select2() {
    	if ( class_exists( 'woocommerce' ) ) {
    		wp_dequeue_style( 'select2' );
    		wp_deregister_style( 'select2' );
    		wp_dequeue_style( 'selectWoo' );
    		wp_deregister_style( 'selectWoo' );
    
    		wp_dequeue_script( 'select2');
    		wp_deregister_script('select2');
    		wp_dequeue_script( 'selectWoo');
    		wp_deregister_script('selectWoo');
    	} 
    } 
    add_action( 'wp_enqueue_scripts', 'bones_dequeue_stylesandscripts_select2', 100 );
    
    1. Brilliant, thanks for sharing!

  10. Hi! I’ve copied the 2nd snippet exactly (convenient that I need the PA option!) and it’s not having any impact on my site. It’s in my child theme functions .php.

    Do you have any thoughts on this?

    1. Hey Laurie, thanks for your comment! Yes, the 2nd snippet highly depends on your theme/custom coding so there is something there not triggering. Or, https://businessbloomer.com/woocommerce-allow-shipping-one-state-only/#comment-24806

    2. Darn! Thanks for the info. I tried to go back to the first snippet, which was working and now it’s not. Do you have any advice on that? Thanks!

      1. Laurie, are you using the exact same code? If not, can you show me your custom code?

  11. Heads up to anyone trying snippet #2 to hide states on Checkout…

    It *appears* to work and correctly replaces the shipping states, but I discovered it was preventing Woo from loading our shipping methods when somebody tried to “ship to a different address,” saying “There are no shipping methods available. Please double check your address, or contact us if you need any help.” Removing the snippet fixed the issue.

    It’s happening because Woo uses Select2.js to replace the State dropdown with a bunch of custom ‘s, but the snippet here uses jQuery to put an back onto the page, breaking however Woo sends address info back to calculate shipping.

    Hopefully I’ll have some time to fix it to work with Select2, but in the meantime you should probably remove snippet #2 from here Rodolfo. Thanks for all your helpful coding though!

    1. Thank you so much for your feedback Justin πŸ™‚

    2. i found the same thing. Also, this doesn’t affect the My Account Shipping address. If there’s a solution, please post.

      thanks!

    3. It’s happening to me also. OceanWP theme, Woo 3.1.2, WP 4.8.2.
      The filter on shipping states dropdown *works* but the order cannnot be completed because the shippping methods aren’t displayed: β€œThere are no shipping methods available. Please double check your address, or contact us if you need any help.”

      If someone have a workaround for this would be awesome.

  12. Hi Rodolfo,

    Thank you for your work. If I reside in Canada and im dealing with Provinces. Would i just need to modify the code from “US” to “Canada” and “states” to “provinces and PA to ON (Ontario) ? Thank you for your help.

    1. Hey Paul, thanks for your comment! Correct – you only need to double check the exact 2-letters codes that WooCommerce uses. They’re in the /i18n/states/ folder πŸ™‚

  13. Hi,

    I realize a website for a client and he wants to limit shipping of his burgers to a perimeter of two miles so only people in this perimeter can order.

    Is it possible to do that ?

    1. Hey Romain, thanks so much for your comment! This is pretty complicated – how are you going to measure the “miles” on the checkout page? Maybe by ZIP code? You’ll need to find a rule first and then “translate” that in WooCommerce – not sure how you can manage to do so in this case πŸ™‚

  14. Make sure the states are separated by a comma. The list with all the states is included in this code. Remove the states you do not want. Last state on the list does not require a comma.

    
    
    /**
    * @Control the List of States shown in the Shipping Section of Checkout Page
    */
    
    add_action('wp_footer', 'aelia_checkout_shipping_filter_US_states');
     
    function aelia_checkout_shipping_filter_US_states() {
      if(!is_checkout()) {
        return;
      }
     ?&gt;
     
      jQuery(document).ready(function($) {
     
       $(document.body).on('country_to_state_changed', function(event, args) {
        function set_shipping_states(states) {
         var $shipping_state = $('#shipping_state');
         $shipping_state.find('option:not([value=""])').remove();
     
         for(state in states) {
          $shipping_state.append('' + states[state] + '');
         }
        }
     
        var $shipping_country = $('#shipping_country');
     
        var new_shipping_states = {};
        switch($shipping_country.val()) {
         case 'US':
          new_shipping_states = { 
            'AL': 'Alabama',
    	'AK': 'Alaska', 
    	'AZ': 'Arizona', 
    	'AR': 'Arkansas', 
    	'CO': 'Colorado', 
    	'CT': 'Connecticut', 
    	'DE': 'Delaware', 
    	'DC': 'District Of Columbia', 
    	'FL': 'Florida', 
    	'GA': 'Georgia', 
    	'HI': 'Hawaii', 
    	'ID': 'Idaho', 
    	'IL': 'Illinois', 
    	'IN': 'Indiana', 
    	'IA': 'Iowa', 
    	'KS': 'Kansas', 
    	'KY': 'Kentucky', 
    	'LA': 'Louisiana', 
    	'ME': 'Maine', 
    	'MD': 'Maryland', 
    	'MA': 'Massachusetts', 
    	'MI': 'Michigan', 
    	'MN': 'Minnesota', 
    	'MS': 'Mississippi', 
    	'MO': 'Missouri', 
    	'MT': 'Montana', 
    	'NE': 'Nebraska', 
    	'NV': 'Nevada', 
    	'NH': 'New Hampshire', 
    	'NJ': 'New Jersey', 
    	'NM': 'New Mexico', 
    	'NY': 'New York', 
    	'NC': 'North Carolina', 
    	'ND': 'North Dakota', 
    	'OH': 'Ohio', 
    	'OK': 'Oklahoma', 
    	'PA': 'Pennsylvania', 
    	'RI': 'Rhode Island', 
    	'SC': 'South Carolina', 
    	'SD': 'South Dakota', 
    	'TN': 'Tennessee', 
    	'TX': 'Texas', 
    	'UT': 'Utah', 
    	'VT': 'Vermont', 
    	'VA': 'Virginia', 
    	'WV': 'West Virginia', 
    	'WI': 'Wisconsin', 
    	'WY': 'Wyoming',
    	'AA': 'Armed Forces (AA)', 
    	'AE': 'Armed Forces (AE)', 
    	'AP': 'Armed Forces (AP)'
          }
          break;
        }
     
        if(!$.isEmptyObject(new_shipping_states)) {
         set_shipping_states(new_shipping_states);
        }
       });
      });
     
     &lt;?php
    };
    
    
  15. Hi,

    Thanks for the code!
    If i add 2 states, seems not to work.
    Do i need to change something else?

    1. Hey Dan, thanks for your comment! Would you be able to paste your snippet here, as it should definitely work πŸ™‚ Let me know!

      1. Thanks for your replay.
        Here it is:

        
        /**
        * @snippet Allow Shipping to Only One State @ WooCommerce Cart
        * @how-to Get CustomizeWoo.com FREE
        * @sourcecode https://businessbloomer.com/?p=20462
        * @author Diego Zanella
        * @testedwith WooCommerce 2.5.5
        */
         
        add_action('wp_footer', 'aelia_checkout_shipping_filter_US_states');
         
        function aelia_checkout_shipping_filter_US_states() {
          if(!is_checkout()) {
            return;
          }
         ?&gt;
         
          jQuery(document).ready(function($) {
         
           $(document.body).on('country_to_state_changed', function(event, args) {
            function set_shipping_states(states) {
             var $shipping_state = $('#shipping_state');
             $shipping_state.find('option:not([value=""])').remove();
         
             for(state in states) {
              $shipping_state.append('' + states[state] + '');
             }
            }
         
            var $shipping_country = $('#shipping_country');
         
            var new_shipping_states = {};
            switch($shipping_country.val()) {
             case 'US':
              new_shipping_states = {
               'NJ': 'New Jersey'
               'NY': 'New York'
              }
              break;
            }
         
            if(!$.isEmptyObject(new_shipping_states)) {
             set_shipping_states(new_shipping_states);
            }
           });
          });
         
         &lt;?php
        };
        
        

        If i leave only 1, it works fine!

        1. Uhm, you might be missing a comma here:

          new_shipping_states = {
                 'NJ': 'New Jersey',
                 'NY': 'New York'
                }
          

          Let me know πŸ™‚

          1. Thanks again, but did not work.
            If I add one more state, all of them appear in woocommerce.

            With one state it works perfectly.
            Its really strange.

            Any other solution?

            1. Hey πŸ™‚ That’s the correct solution so maybe you have a snippet/plugin/theme conflict. Try troubleshooting this maybe – let me know!

  16. Hi,
    I have a client who has a Flower Shop in the UK. She only wants to sell her flowers to local customers within a 10 mile radius of her shop. I was able to generate a list of all the post codes that this includes.
    However, how would I disable shipping for all customers of the UK outside of this post code list?.

    Any help would be much appreciated.

    tia,
    Andy

    1. Hey Andy, thanks for your comment! Are you on WooCommerce 2.6+ – the new shipping “zones” should help you do exactly that? Let me know

Questions? Feedback? Customization? Leave your comment now!
_____

If you are writing code, please wrap it like so: [php]code_here[/php]. Failure to complying with this, as well as going off topic or not using the English language will result in comment disapproval. You should expect a reply in about 2 weeks - this is a popular blog but I need to get paid work done first. Please consider joining the Business Bloomer Club to get quick WooCommerce support. Thank you!

Your email address will not be published. Required fields are marked *