Many times, e-shop owners need a flexible way to present WooCommerce shipping costs to their users depending on the weight or even the shipping zone. Even though there are many free and premium plugins out there that do an “okay” job, it is far more efficient to use plain WooCommerce’s features along with some php.


Let’s make a scenario where we have 3 possible shipping zones:

  • Hometown
  • Ground Areas
  • Island Destinations

With 2 possible pricing tiers for each zone:

  • A fixed price up to 10kg total cart weight
  • Fixed price plus an extra cost for orders with cart weight above 10kg

Fixed Costs (equal or less than 10kg) :

  • Hometown = 5€
  • Ground Areas = 6€
  • Island Destinations = 8€

Extra Costs (more than 10kg) :

  • Hometown = 0.5€ / kg
  • Ground Areas = 0.7€ / kg
  • Island Destinations = 0.9€ / kg

Solution on Paper

To display the correct shipping costs we should:

  1. Get the Cart Total Weight
  2. Check user’s shipping zone
  3. Check if Cart Total Weight is less or more than 10kg
    1. If Cart Total Weight is less than or equal to 10kg, continue
    2. If Cart Total Weight is more than 10kg, find how many extra kg
  4. Apply either the fixed cost or the fixed cost plus extra cost – depending on Cart Total Weight

Flat Rates on WooCommerce

To do that, we create 3 different shipping zones in WooCommerce as stated above. Then, we set a flat rate shipping method for each shipping zone. We make sure to set the cost for each flat rate to the appropriate fixed cost of the current shipping method (e.g. Flat rate of Hometown is 5€).

Then we need to get the flat rate ID codes for each of the 3 flat rates. To do that, we “right click” -> “inspect” the name of the flat rate:

Last element of popup should be “inspect” in English

On HTML element inspector it should highlight the code you’re inspecting, and if you check the parent elements you should see a data-id like this:

Flat Rate Cost ID of Current Shipping Zone is 11

Now, we should write down the slugs that we will use for shipping zone if-conditionals. To do that, we write ‘flat_rate:ID’ replacing ID with the IDs we got. So, after checking all my 3 flat rates, I have the following slugs:

  • Hometown: flat_rate:9
  • Ground Areas: flat_rate:1
  • Island Destinations: flat_rate:4

Adding the Code

Firstly, you should know that whenever the user inputs an area, WooCommerce automatically updates the cart and by default it applies the first available rate to it. So, for example, if I input an ‘Island Destinations’ area, WooCommerce will update the cart and apply the ID of the first available rate. So, if we have just one available rate (our flat rate), then the cart will have flat_rate:4. We are going to use that in if-conditionals to determine which shipping zone is selected.

In our functions.php we add:

<?php // Don't copy opening tag

function variable_wc_costs( $rates, $package ) {
    // Store Cart Total Weight in a variable for ease of use
    $cart_weight = WC()->cart->get_cart_contents_weight();

    if ( $cart_weight <= 10 ) { // Equal or less than 10kg

        if (isset($rates['flat_rate:9'])) // Hometown
            $rates['flat_rate:9']->cost = 5; // We set the initial cost (e.g. 5€)
        elseif (isset($rates['flat_rate:1'])) // Ground Areas
            $rates['flat_rate:1']->cost = 6; // We set the initial cost (e.g. 6€)
        elseif (isset($rates['flat_rate:4'])) // Island Destinations
            $rates['flat_rate:4']->cost = 8; // We set the initial cost (e.g. 8€)

    } else { // More than 10kg
        // How many more kg?
        $extra_kg = $cart_weight - 10;

        if (isset($rates['flat_rate:9']))
            $rates['flat_rate:9']->cost = 5 + $extra_kg * 0.5; // + 0.5€ extra cost per kg
        elseif (isset($rates['flat_rate:1']))
            $rates['flat_rate:1']->cost = 6 + $extra_kg * 0.7; // + 0.7€ extra cost per kg
        elseif (isset($rates['flat_rate:4']))
            $rates['flat_rate:4']->cost = 8 + $extra_kg * 0.9; // + 0.9€ extra cost per kg

    return $rates;
add_filter( 'woocommerce_package_rates', 'variable_wc_costs' );

And we are done!