title: Magento 2 add new field to shipping form
body: <p>I'm trying to add a new field to the shipping address in Magento 2.3.4.
  I would like to add it "Magento way" that's why I used these tutorials:</p>
  
  <p><a href="https://devdocs.magento.com/guides/v2.3/howdoi/checkout/checkout_new_field.html" rel="nofollow noreferrer">https://devdocs.magento.com/guides/v2.3/howdoi/checkout/checkout_new_field.html</a>
  <a href="https://www.edmondscommerce.co.uk/handbook/Platforms/Magento-2/Guides/Custom-Shipping-Address-Field/" rel="nofollow noreferrer">https://www.edmondscommerce.co.uk/handbook/Platforms/Magento-2/Guides/Custom-Shipping-Address-Field/</a></p>
  
  <p>The field appears correctly on the frontend, but after adding new address and filling that field and click "Ship here":
  <a href="https://i.stack.imgur.com/4vBTc.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/4vBTc.png" alt="enter image description here"></a>
  I got that error:
  <a href="https://i.stack.imgur.com/HCmVR.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/HCmVR.png" alt="enter image description here"></a>
  Here is my code:</p>
  
  <p>1) At first, I thought that it is not necessary (this step is not in Magento 2 devdocs but appears in second tutorial) - app/code/Company/Module/Setup/Patch/Data/AddVipCodeAttribute.php:</p>
  
  <pre><code>&lt;?php
  
  namespace Company\Module\Setup\Patch\Data;
  
  use Magento\Catalog\Model\Product;
  use Magento\Customer\Setup\CustomerSetupFactory;
  use Magento\Eav\Setup\EavSetup;
  use Magento\Eav\Setup\EavSetupFactory;
  use Magento\Framework\DB\Ddl\Table;
  use Magento\Framework\Setup\ModuleDataSetupInterface;
  use Magento\Framework\Setup\Patch\DataPatchInterface;
  use Magento\Framework\Setup\Patch\PatchRevertableInterface;
  
  class AddVipCodeAttribute implements DataPatchInterface, PatchRevertableInterface
  {
      /**
       * @var ModuleDataSetupInterface
       */
      private $moduleDataSetup;
  
      /**
       * @var EavSetupFactory
       */
      private $eavSetupFactory;
  
      /**
       * @var CustomerSetupFactory
       */
      private $customerSetupFactory;
  
      /**
       * Constructor
       *
       * @param ModuleDataSetupInterface $moduleDataSetup
       * @param EavSetupFactory $eavSetupFactory
       */
      public function __construct(
          ModuleDataSetupInterface $moduleDataSetup,
          EavSetupFactory $eavSetupFactory,
          CustomerSetupFactory $customerSetupFactory
      ) {
          $this-&gt;moduleDataSetup = $moduleDataSetup;
          $this-&gt;eavSetupFactory = $eavSetupFactory;
          $this-&gt;customerSetupFactory = $customerSetupFactory;
      }
  
      /**
       * @inheritdoc
       */
      public function apply()
      {
          $this-&gt;moduleDataSetup-&gt;getConnection()-&gt;startSetup();
          /** @var CustomerSetup $customerSetup */
          $customerSetup = $this-&gt;customerSetupFactory-&gt;create(['setup' =&gt; $this-&gt;moduleDataSetup]);
          $customerSetup-&gt;addAttribute('customer_address', 'vip_code', [
              'label' =&gt; 'Vip account code',
              'input' =&gt; 'text',
              'type' =&gt; Table::TYPE_TEXT,
              'source' =&gt; '',
              'required' =&gt; false,
              'position' =&gt; 333,
              'visible' =&gt; true,
              'system' =&gt; false,
              'is_used_in_grid' =&gt; false,
              'is_visible_in_grid' =&gt; false,
              'is_filterable_in_grid' =&gt; false,
              'is_searchable_in_grid' =&gt; false,
              'backend' =&gt; ''
          ]);
          $attribute = $customerSetup-&gt;getEavConfig()-&gt;getAttribute('customer_address', 'vip_code')
              -&gt;addData(['used_in_forms' =&gt; [
                  'adminhtml_customer_address',
                  'adminhtml_customer',
                  'customer_address_edit',
                  'customer_register_address',
                  'customer_address',
              ]]);
          $attribute-&gt;save();
          $this-&gt;moduleDataSetup-&gt;getConnection()-&gt;endSetup();
      }
  
      /**
       * @inheritDoc
       */
      public function revert()
      {
      }
  
      /**
       * @inheritdoc
       */
      public function getAliases()
      {
          return [];
      }
  
      /**
       * @inheritdoc
       */
      public static function getDependencies()
      {
          return [];
      }
  }
  </code></pre>
  
  <p>2)I created a plugin class:
  etc/frontend/di.xml</p>
  
  <pre><code>&lt;config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"&gt;
      &lt;type name="Magento\Checkout\Block\Checkout\LayoutProcessor"&gt;
          &lt;plugin name="add_custom_field_checkout_form" type="Company\Module\Plugin\Checkout\LayoutProcessor" sortOrder="100"/&gt;
      &lt;/type&gt;
  &lt;/config&gt;
  </code></pre>
  
  <p>And layout processor app/code/Company/Module/Plugin/Checkout/LayoutProcessor.php</p>
  
  <pre><code>&lt;?php
  
  namespace Company\Module\Plugin\Checkout;
  
  use Magento\Checkout\Block\Checkout\LayoutProcessor as LayoutProcessorCore;
  use Magento\Customer\Model\Session;
  
  class LayoutProcessor
  {
      /**
       * @var Session
       */
      protected $session;
  
      public function __construct(
          Session $session
      ) {
          $this-&gt;session = $session;
      }
  
      /**
       * @param LayoutProcessorCore $subject
       * @param array $jsLayout
       *
       * @return array
       */
      public function afterProcess(
          LayoutProcessorCore $subject,
          array  $jsLayout
      ) {
              $customAttributeCode = 'vip_code';
              $customField = [
                  'component' =&gt; 'Magento_Ui/js/form/element/abstract',
                  'config' =&gt; [
                      // customScope is used to group elements within a single form (e.g. they can be validated separately)
                      'customScope' =&gt; 'shippingAddress.custom_attributes',
                      'customEntry' =&gt; null,
                      'template' =&gt; 'ui/form/field',
                      'elementTmpl' =&gt; 'ui/form/element/input',
                      'tooltip' =&gt; [
                          'description' =&gt; 'Vip accounts code. Example: 123123123ASD',
                      ],
                  ],
                  'dataScope' =&gt; 'shippingAddress.custom_attributes' . '.' . $customAttributeCode,
                  'label' =&gt; 'Vip code',
                  'provider' =&gt; 'checkoutProvider',
                  'sortOrder' =&gt; 0,
                  'validation' =&gt; [
                      'required-entry' =&gt; false
                  ],
                  'options' =&gt; [],
                  'filterBy' =&gt; null,
                  'customEntry' =&gt; null,
                  'visible' =&gt; true,
              ];
              $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
                  ['shippingAddress']['children']['shipping-address-fieldset']['children'][$customAttributeCode] = $customField;
  
          return $jsLayout;
      }
  }
  </code></pre>
  
  <p>3)JS files:</p>
  
  <p>app/code/Company/Module/view/frontend/requirejs-config.js</p>
  
  <pre><code>var config = {
      config: {
          mixins: {
              'Magento_Checkout/js/action/set-shipping-information': {
                  'Company_Module/js/add-new-field': true
              }
          }
      }
  };
  
  </code></pre>
  
  <p>app/code/Company/Module/view/frontend/web/js/add-new-field.js</p>
  
  <pre><code>/*jshint browser:true jquery:true*/
  /*global alert*/
  define([
      'jquery',
      'mage/utils/wrapper',
      'Magento_Checkout/js/model/quote'
  ], function ($, wrapper, quote) {
      'use strict';
  
      return function (setShippingInformationAction) {
  
          return wrapper.wrap(setShippingInformationAction, function (originalAction) {
              var shippingAddress = quote.shippingAddress();
              if (shippingAddress['extension_attributes'] === undefined) {
                  shippingAddress['extension_attributes'] = {};
              }
  
              shippingAddress['extension_attributes']['vip_code'] = shippingAddress.customAttributes['vip_code'];
              console.log(shippingAddress);
              // pass execution to original action ('Magento_Checkout/js/action/set-shipping-information')
              return originalAction();
          });
      };
  });
  
  </code></pre>
  
  <p><strong>[UPDATE]</strong>
  app/code/BartCompany/VipAccounts/etc/extension_attributes.xml:</p>
  
  <pre><code>&lt;?xml version="1.0"?&gt;
  &lt;config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd"&gt;
      &lt;extension_attributes for="Magento\Quote\Api\Data\AddressInterface"&gt;
          &lt;attribute code="vip_code" type="string" /&gt;
      &lt;/extension_attributes&gt;
  &lt;/config&gt;
  </code></pre>
  
  <p>Can anybody help me with that, I'm working on it almost a week, and tried a lot of solutions, but it is for me very important to do that by custom_attributes field.
  I will be grateful, thank You.</p>