Having several different shipping providers on your site improves the user experience, which is crucial for converting customers into buyers. This article will show you how to group the shipping methods by carrier provider and allow your users to choose their preferred shipping method.
If your site has several shipping vendors, it must be making quite some sales! One often overlooked aspect of the shipping process is how shipping options are presented to customers, especially if there are a large number of shipping methods. However, these can be reorganized to improve the UX for your customers. I’ll explain how.
Create Shipping Methods Mixin
To begin with, create requirejs-config.js
in Magento_Checkout
folder in your theme and paste the following code:
var config = {
config: {
mixins: {
'Magento_Checkout/js/view/shipping': {
'Magento_Checkout/js/view/shipping-mixin': true
}
}
}
}
Group Shipping Methods by Barrier Code
In shipping-mixin.js
file, paste the following code:
define([
], function () {
'use strict';
return function (Shipping) {
return Shipping.extend({
carriers: function () {
const carrierCodes = this.rates.map(item => item.carrier_code).filter((value, index, self) => self.indexOf(value) === index)
const carriers = carrierCodes.map(carrierCode => this.rates.filter(rate => rate.carrier_code === carrierCode));
return carriers;
}
})
}
})
What the snippet will do is as follows:
- variable
carrierCodes
will get ahold of unique carrier codes only - variable
carriers
will group all shipping methods that belong to the same carrier
See output in DevTools to see the actual data:
Modify Template Files
In order to complete the change, it is necessary to customize two files. Copy shipping-method-list.html
and shipping-method-item.html
files located in vendor/magenot/module-checkout/view/frontend/web/template/shipping-addres
folder into your own theme.
In shipping-method-list.html
the carriers()
variable is used from the mixin.
<div id="checkout-shipping-method-load">
<table class="table-checkout-shipping-method">
<tbody>
<!-- ko foreach: { data: carriers(), as: 'carrier'} -->
<!--ko template: { name: element.shippingMethodItemTemplate} --><!-- /ko -->
<!-- /ko -->
</tbody>
</table>
</div>
Next, paste the following snippet into shipping-method-item.html
. If needed, this snippet should be adjusted per the design requirements of the project itself. The only requirement is to ensure the click event listener is working properly (which can be checked in the Billing step (step 2).
<!-- ko foreach: { data: carrier, as: 'method'} -->
<!-- ko if: ($index() === 0) -->
<tr class="shipping-carrier-title">
<td colspan="2">
<strong data-bind="text: method.carrier_title"></strong>
</td>
</tr>
<!-- /ko -->
<tr class="shipping-method-details" click="element.selectShippingMethod">
<td class="shipping-method-title"
attr="'id': 'label_method_' + method.method_code + '_' + method.carrier_code">
<div class="choice">
<input type="radio"
class="radio"
ifnot="method.error_message"
ko-checked="element.isSelected"
ko-value="method.carrier_code + '_' + method.method_code"
attr="'aria-labelledby': 'label_method_' + method.method_code + '_' + method.carrier_code + ' ' + 'label_carrier_' + method.method_code + '_' + method.carrier_code,
'checked': element.rates().length == 1 || element.isSelected,
'id': 'label_method_' + method.method_code + '_' + method.carrier_code + ' ' + 'label_carrier_' + method.method_code + '_' + method.carrier_code"
/>
<label class="label"
data-bind="
text: method.method_title,
attr: {
for: 'label_method_' + method.method_code + '_' + method.carrier_code + ' ' + 'label_carrier_' + method.method_code + '_' + method.carrier_code
}"
></label>
</div>
<div if="method.error_message" class="shipping-method-message">
<div role="alert" class="message error">
<div text="method.error_message"></div>
</div>
<span class="no-display">
<input type="radio"
attr="'value' : method.method_code, 'id': 's_method_' + method.method_code">
</span>
</div>
</td>
</td>
<!-- ko ifnot: (method.error_message) -->
<td class="shipping-method-price">
<each args="element.getRegion('price')" render=""></each>
</td>
<!-- /ko -->
</tr>
<!-- /ko -->
The last part is to make some minor styling adjustments to override default styling and make things look better. ๐
.table-checkout-shipping-method {
width: 100%;
}
.shipping-method-title {
width: auto !important;
}
.shipping-carrier-title td {
padding-bottom: 0 !important;
border-top: 0 !important;
}
.shipping-method-details td {
border-top: none !important;
padding-bottom: 0 !important;
}
.shipping-method-details .choice {
display: inline-flex;
align-items: center;
}
Implementation in a Custom Project
To demonstrate code changes in best possible way, let’s see this implementation of shipping methods with actual vendors and several shipping methods. Here is the actual display of how it looks like on a Blank theme using UPS, USPS, FedEx. Store Pickup is not the default one, but of Amasty (Amasty_StorePickup).
Believe it or not, but that’s it! Nothing too complicated, isn’t it? ๐
If you have any questions, leave a comment below. Until next time! ๐