Build Your Own Magento 2 Shipping Method

Build Your Own Magento 2 Shipping Method

Creating a custom shipping method in Magento 2 gives you control over delivery options that fit your business model. You set the rules, pricing, and conditions without relying on third-party carriers. This guide walks you through building a custom Magento 2 shipping method from scratch.

When You Need Custom Shipping Options

Research shows 61% of shoppers abandon their carts when unexpected shipping costs appear, and 46.5% of businesses report higher profits after offering free shipping. Your shipping strategy directly impacts your bottom line.

You'll need a custom shipping method when:

  • You handle deliveries yourself instead of using external carriers
  • Default Magento shipping options don't match your business requirements
  • You sell on Amazon or eBay and import orders through third-party plugins
  • You want to offer region-specific delivery rates
  • You need to implement conditional shipping logic based on order value, weight, or destination

Module Structure Overview

Your custom shipping module requires three core files plus the standard module setup files (module.xml, registration.php, composer.json). Name your module something clear like YourNamespace_CustomShipping.

Required Files Breakdown

File Location Purpose
system.xml etc/adminhtml/Admin configuration settings
config.xml etc/Default values and model declaration
Carrier class Model/Carrier/Shipping logic and rate calculation
module.xml etc/Module registration
registration.php Root Module registration

Tip

To enhance your eCommerce store’s performance with Magento, focus on optimizing site speed by utilizing Emmo themes and extensions. These tools are designed for efficiency, ensuring your website loads quickly and provides a smooth user experience. Start leveraging Emmo's powerful solutions today to boost customer satisfaction and drive sales!

Configure Admin Settings

Create system.xml at app/code/YourNamespace/CustomShipping/etc/adminhtml/system.xml. This file adds configuration fields to your admin panel under Stores > Configuration > Sales > Delivery Methods.

The critical part: your settings must nest under the carriers section. This tells Magento you're creating a shipping method, not just any configuration.

<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">

<system>

<section id="carriers" translate="label" type="text" sortOrder="100"

showInDefault="1" showInWebsite="1" showInStore="1">

<group id="custom_ship" translate="label" type="text" sortOrder="2"

showInDefault="1" showInWebsite="1" showInStore="1">

<label>Your Custom Shipping</label>

<field id="active" translate="label" type="select" sortOrder="1"

showInDefault="1" showInWebsite="1" showInStore="0">

<label>Enabled</label>

<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>

</field>

<field id="name" translate="label" type="text" sortOrder="2"

showInDefault="1" showInWebsite="1" showInStore="1">

<label>Method Name</label>

</field>

<field id="title" translate="label" type="text" sortOrder="3"

showInDefault="1" showInWebsite="1" showInStore="1">

<label>Title</label>

</field>

<field id="price" translate="label" type="text" sortOrder="4"

showInDefault="1" showInWebsite="1" showInStore="0">

<label>Shipping Price</label>

<validate>validate-number validate-zero-or-greater</validate>

</field>

</group>

</section>

</system>

</config>

These fields appear in your admin panel:

  • Enabled: Toggle shipping method on/off
  • Method Name: Internal identifier
  • Title: What customers see at checkout
  • Shipping Price: Fixed rate to charge

Set Default Configuration Values

Create config.xml at app/code/YourNamespace/CustomShipping/etc/config.xml. This file links your admin settings to your shipping logic through the model class.

<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">

<default>

<carriers>

<custom_ship>

<active>1</active>

<model>YourNamespace\CustomShipping\Model\Carrier\CustomShip</model>

<name>Custom Delivery</name>

<title>Custom Delivery Service</title>

<price>10</price>

</custom_ship>

</carriers>

</default>

</config>

The <model> tag is crucial. It points to your carrier class that handles rate calculation and availability checks.

Build the Carrier Class

Create your carrier model at app/code/YourNamespace/CustomShipping/Model/Carrier/CustomShip.php. This class implements the shipping logic.

<?php

namespace YourNamespace\CustomShipping\Model\Carrier;

use Magento\Quote\Model\Quote\Address\RateRequest;

use Magento\Shipping\Model\Carrier\AbstractCarrier;

use Magento\Shipping\Model\Carrier\CarrierInterface;

use Magento\Framework\App\Config\ScopeConfigInterface;

use Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory;

use Psr\Log\LoggerInterface;

use Magento\Shipping\Model\Rate\ResultFactory;

use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory;

class CustomShip extends AbstractCarrier implements CarrierInterface

{

const CODE = 'custom_ship';

protected $_code = self::CODE;

protected $rateResultFactory;

protected $rateMethodFactory;

public function __construct(

ScopeConfigInterface $scopeConfig,

ErrorFactory $rateErrorFactory,

LoggerInterface $logger,

ResultFactory $rateResultFactory,

MethodFactory $rateMethodFactory,

array $data = []

) {

$this->rateResultFactory = $rateResultFactory;

$this->rateMethodFactory = $rateMethodFactory;

parent::__construct($scopeConfig, $rateErrorFactory, $logger, $data);

}

public function getAllowedMethods()

{

return [$this->_code => $this->getConfigData('name')];

}

public function collectRates(RateRequest $request)

{

// Check if method is enabled in admin

if (!$this->getConfigData('active')) {

return false;

}

// Create rate result container

$result = $this->rateResultFactory->create();

// Create shipping method

$method = $this->rateMethodFactory->create();

// Set carrier code and title

$method->setCarrier($this->_code);

$method->setCarrierTitle($this->getConfigData('title'));

// Set method code and title

$method->setMethod($this->_code);

$method->setMethodTitle($this->getConfigData('name'));

// Set shipping price from config

$shippingPrice = $this->getConfigData('price');

$method->setPrice($shippingPrice);

$method->setCost($shippingPrice);

// Add method to result

$result->append($method);

return $result;

}

}

How the Carrier Class Works

Your carrier class extends AbstractCarrier and implements CarrierInterface. This inheritance gives you access to Magento's shipping framework.

Key methods:

  • getAllowedMethods(): Returns available shipping options for this carrier
  • collectRates(): Calculates and returns shipping rates

The collectRates() method runs when customers reach checkout. It checks if your method is enabled, then creates a rate object with your pricing.

You use two factory classes:

  • ResultFactory: Container holding all shipping rates
  • MethodFactory: Individual shipping method with price and title

Advanced Rate Calculation

The basic example uses a fixed price. You can add dynamic calculations based on:

  • Order subtotal: $request->getPackageValue()
  • Order weight: $request->getPackageWeight()
  • Destination: $request->getDestCountryId(), $request->getDestRegionId()
  • Cart items: $request->getAllItems()

Example dynamic pricing:

public function collectRates(RateRequest $request)

{

if (!$this->getConfigData('active')) {

return false;

}

$result = $this->rateResultFactory->create();

$method = $this->rateMethodFactory->create();

$method->setCarrier($this->_code);

$method->setCarrierTitle($this->getConfigData('title'));

$method->setMethod($this->_code);

$method->setMethodTitle($this->getConfigData('name'));

// Calculate price based on order weight

$weight = $request->getPackageWeight();

$basePrice = (float)$this->getConfigData('price');

if ($weight > 5) {

$shippingPrice = $basePrice + (($weight - 5) * 2);

} else {

$shippingPrice = $basePrice;

}

$method->setPrice($shippingPrice);

$method->setCost($shippingPrice);

$result->append($method);

return $result;

}

This example charges a base rate plus extra for orders over 5 units of weight.

Enable and Test Your Module

Run these commands from your Magento root directory:

<

php bin/magento module:enable YourNamespace_CustomShipping

php bin/magento setup:upgrade

php bin/magento setup:di:compile

php bin/magento setup:static-content:deploy -f

php bin/magento cache:flush

After deployment, check three places:

Admin configuration: Stores > Configuration > Sales > Delivery Methods. Your shipping method should appear with your configured fields.

Admin order creation: Create a new order from the admin panel. Your shipping method should appear in the shipping options.

Frontend checkout: Add items to cart and proceed to checkout. Your method should display with the configured

Enable Shipment Tracking

To add tracking functionality, include the isTrackingAvailable() method in your carrier class:

public function isTrackingAvailable()

{

    return true;

}

This enables your shipping method in the shipment tracking dropdown when creating shipments.

Common Issues and Fixes

Method doesn't appear at checkout:

  • Verify active is set to 1 in config.xml
  • Check that your carrier code matches across all files
  • Clear cache after code changes

Configuration not saving:

  • Ensure system.xml is in the correct adminhtml directory
  • Check XML syntax for errors
  • Verify section id is carriers

Price not updating:

  • Clear cache after configuration changes
  • Check getConfigData() calls use correct field names
  • Verify price field has proper validation

Extending for Multiple Methods

You can create multiple shipping methods by modifying the collectRates() method to append multiple rate objects. Each method needs unique carrier and method codes.

public function collectRates(RateRequest $request)

{

if (!$this->getConfigData('active')) {

return false;

}

$result = $this->rateResultFactory->create();

// Standard shipping

$standard = $this->rateMethodFactory->create();

$standard->setCarrier($this->_code);

$standard->setCarrierTitle($this->getConfigData('title'));

$standard->setMethod('standard');

$standard->setMethodTitle('Standard Delivery');

$standard->setPrice(10);

$result->append($standard);

// Express shipping

$express = $this->rateMethodFactory->create();

$express->setCarrier($this->_code);

$express->setCarrierTitle($this->getConfigData('title'));

$express->setMethod('express');

$express->setMethodTitle('Express Delivery');

$express->setPrice(25);

$result->append($express);

return $result;

}

Conclusion

You now have a functional custom shipping method in Magento 2. You can extend this foundation by adding conditional logic, integrating with external APIs for real-time rates, or implementing complex calculation rules based on your business needs.

FAQs

What does it mean to build a custom shipping method in Magento 2?

Building a custom shipping method in Magento 2 means creating your own carrier module that defines how shipping rates are calculated and displayed during checkout, offering flexibility beyond the default Magento shipping options.

Why would I create a custom shipping method?

Custom shipping methods allow you to tailor delivery options based on your business model — for example, offering region-specific delivery, custom rate logic, or integrations with external shipping APIs that Magento doesn’t support by default.

Where do I define a custom shipping method in Magento 2?

You define it inside a custom module under the app/code/ directory. Essential configuration files like etc/config.xml, etc/adminhtml/system.xml, and a carrier model class handle the setup, logic, and admin options.

Which files are essential to create a shipping method module?

Core files include registration.php, etc/module.xml for module registration, etc/config.xml for settings, etc/adminhtml/system.xml for admin configuration, and the main carrier class inside Model/Carrier/.

How do I register a custom shipping method in Magento 2?

Register your module with registration.php and etc/module.xml, then enable it using bin/magento setup:upgrade. Once activated, your new shipping method appears in the admin panel under Stores → Configuration → Sales → Shipping Methods.

Can I add multiple shipping options under one carrier?

Yes. You can define multiple methods—like “Standard” and “Express”—within one carrier class by extending the collectRates() function and appending each method with different titles, prices, and conditions.

How do I calculate custom shipping rates?

In your carrier model’s collectRates() function, implement custom logic based on product weight, subtotal, destination, or any business rule. You can set rates dynamically or pull data from external APIs.

What if my custom shipping method doesn’t appear at checkout?

Ensure the module is active, the XML configurations are valid, and caching is cleared using bin/magento cache:flush. Also, check if your carrier’s collectRates() function is returning a proper result object.

Can I restrict my shipping method to specific regions or products?

Yes. You can add conditions in your collectRates() method or use Magento’s configuration settings to limit availability based on countries, postcodes, or product attributes.

What are best practices for maintaining a custom shipping method?

Follow Magento coding standards, use dependency injection for clean architecture, document rate logic, and test thoroughly after Magento updates to ensure compatibility and performance consistency.