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?

This time, I’ve reached out to Diego Zanella, a WooCommerce genius who is also the author of the Aelia Currency Switcher plugin for WooCommerce.

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


/**
 * @snippet       Allow Shipping to Only One State @ WooCommerce Cart Shipping Calculator
 * @how-to        Watch tutorial @ https://businessbloomer.com/?p=19055
 * @sourcecode    https://businessbloomer.com/?p=20462
 * @author        Diego Zanella
 * @compatible    WooCommerce 3.5.6
 * @donate $9     https://businessbloomer.com/bloomer-armada/
 */

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

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

Note: might not work – see comment https://businessbloomer.com/woocommerce-allow-shipping-one-state-only/#comment-24806


/**
 * @snippet       Allow Shipping to Only One State @ WooCommerce Cart
 * @how-to        Watch tutorial @ https://businessbloomer.com/?p=19055
 * @sourcecode    https://businessbloomer.com/?p=20462
 * @author        Diego Zanella
 * @compatible    WooCommerce 3.5.6
 * @donate $9     https://businessbloomer.com/bloomer-armada/
 */

add_action( 'wp_footer', 'aelia_checkout_shipping_filter_US_states' );

function aelia_checkout_shipping_filter_US_states() {
  if( !is_checkout() ) {
    return;
  }
 ?>
 <script type="text/javascript">
  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 = {};
    switch($shipping_country.val()) {
     case 'US':
      new_shipping_states = {
       'PA': 'Pennsylvania'
      }
      break;
    }
    if(!$.isEmptyObject(new_shipping_states)) {
     set_shipping_states(new_shipping_states);
    }
   });
  });
 </script>
 <?php
}

WooCommerce: Allow Shipping to Only One State @ Checkout
WooCommerce: Allow Shipping to Only One State @ Checkout

Where to add this code?

You can place PHP snippets at the bottom of your child theme functions.php file (before "?>" if you have it). CSS, on the other hand, goes in your child theme style.css file. Make sure you know what you are doing when editing such delicate files - if you need more guidance, please take a look at my free WooCommerce Customization video tutorial.

Does this snippet still work?

Please let me know in the comments if everything worked as expected. I would be happy to revise the snippet if you report otherwise (please provide screenshots). I have tested this code with Storefront theme, the WooCommerce version listed above and a WordPress-friendly hosting on PHP 7.

If you think this code saved you time & money, please join other Business Bloomer supporters and avail of 365 days of WooCommerce benefits. Thank you in advance :)

Need Help with WooCommerce?

Check out these free videos, tutorials and tips!

  • how-to-edit-woocommerce-with-php-snippets
  • woocommerce-hooks-add_action-list-visual
  • woocommerce-customize-single-product-page-PHP
  • woocommerce-customize-shop-page-PHP
  • woocommerce-advanced-customization
  • how-to-edit-woocommerce-cart
  • woocommerce-customize-checkout-page-PHP
  • woocommerce-email-customization
  • woocommerce-conditional-logic

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

Author, WooCommerce expert and WordCamp speaker, Rodolfo has worked as a 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.

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

  1. 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 πŸ™‚

  2. 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 + '">
      
  3. 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!

  4. 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!

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

  5. 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.

  6. 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 πŸ™‚

  7. 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 πŸ™‚

  8. 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
    };
    
    
  9. 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!

    2. Thanks for your replay.
      Here it is:

      
      /**
      * @snippet Allow Shipping to Only One State @ WooCommerce Cart
      * @how-to Watch tutorial @ https://businessbloomer.com/?p=19055
      * @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!

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

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

      Let me know πŸ™‚

    4. 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?

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

  10. 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? Support? Leave your Comment Now!
_____

If you are writing code, please wrap it between: [php]code_here[/php]. Failure to complying with this (as well as going off topic) will result in comment deletion. You should expect a reply in about a week - this is a popular blog but I need to get paid work done first. Please consider joining #BloomerArmada to ask me 1-to-1 WooCommerce questions. Thank you :)

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.