diff --git a/src/data/navigation/sections/tutorials.js b/src/data/navigation/sections/tutorials.js
index a826f36ff..ead44dac0 100644
--- a/src/data/navigation/sections/tutorials.js
+++ b/src/data/navigation/sections/tutorials.js
@@ -143,6 +143,11 @@ module.exports = [
           title: "Modify media library folder permissions",
           path: "/tutorials/backend/modify-image-library-permissions/",
         },
+        {
+          title: "Create a custom product attribute input type",
+          path: "/tutorials/backend/create-custom-attribute-input-type/",
+        },
       ],
     },
-  ];
\ No newline at end of file
+  ];
+
diff --git a/src/pages/_images/tutorials/custom-attribute-input-type.png b/src/pages/_images/tutorials/custom-attribute-input-type.png
new file mode 100644
index 000000000..51da25aa6
Binary files /dev/null and b/src/pages/_images/tutorials/custom-attribute-input-type.png differ
diff --git a/src/pages/_images/tutorials/product-tags-attribute-final-result.png b/src/pages/_images/tutorials/product-tags-attribute-final-result.png
new file mode 100644
index 000000000..456490679
Binary files /dev/null and b/src/pages/_images/tutorials/product-tags-attribute-final-result.png differ
diff --git a/src/pages/tutorials/backend/create-custom-attribute-input-type.md b/src/pages/tutorials/backend/create-custom-attribute-input-type.md
new file mode 100644
index 000000000..e5b5e4c30
--- /dev/null
+++ b/src/pages/tutorials/backend/create-custom-attribute-input-type.md
@@ -0,0 +1,464 @@
+---
+title: Create a Custom Product Attribute Input Type | Commerce PHP Extensions
+description: Follow this tutorial to create a custom product attribute input type with an Adobe Commerce or Magento Open Source extension.
+contributor_name: Goivvy LLC
+contributor_link: https://www.goivvy.com/
+---
+
+# Create a custom product attribute input type
+
+This tutorial shows you how to add a custom product attribute input type.
+
+We will create an extension that adds a `Dynamic Array` attribute input type and a product attribute `Product Tags` of that type.
+
+The final result will look like this:
+
+
+
+
+
+The final extension is available on [Github](https://github.com/goivvy/new-attribute-type).
+
+## Add a new product attribute input type
+
+To define a new input type, we need to modify arguments of `Magento\Eav\Model\Adminhtml\System\Config\Source\Inputtype` and setup a new UI modifier.
+
+***`app/code/Goivvy/Attribute/etc/adminhtml/di.xml`***:
+
+```xml
+
+
+    
+        
+            
+                - 
+                    
- dynamicarray
 
+                    - Dynamic Array
 
+                 
+            
+        
+    
+    
+        
+            
+                - 
+                    
- Goivvy\Attribute\Ui\DataProvider\Product\Form\Modifier\Dynamic
 
+                    - 50
 
+                 
+            
+        
+    
+
+```
+
+***`app/code/Goivvy/Attribute/Ui/DataProvider/Product/Form/Modifier/Dynamic.php`***:
+
+```php
+locator = $locator;
+        $this->eavAttributeFactory = $eavAttributeFactory;
+    }
+    
+    public function modifyData(array $data)
+    {
+        return $data;
+    }
+
+    public function modifyMeta(array $meta)
+    {
+        foreach ($meta as $groupCode => $groupConfig) {
+            $meta[$groupCode] = $this->modifyMetaConfig($groupConfig);
+        }
+
+        return $meta;
+    }
+
+    protected function modifyMetaConfig(array $metaConfig)
+    {
+        if (isset($metaConfig['children'])) {
+            foreach ($metaConfig['children'] as $attributeCode => $attributeConfig) {
+                if ($this->startsWith($attributeCode, self::CONTAINER_PREFIX)) {
+                    $metaConfig['children'][$attributeCode] = $this->modifyMetaConfig($attributeConfig);
+                } elseif (!empty($attributeConfig['arguments']['data']['config']['formElement']) &&
+                    $attributeConfig['arguments']['data']['config']['formElement'] === static::FORM_ELEMENT_WEEE
+                ) {
+                    $metaConfig['children'][$attributeCode] =
+                        $this->modifyAttributeConfig($attributeCode, $attributeConfig);
+                }
+            }
+        }
+
+        return $metaConfig;
+    }
+
+    protected function modifyAttributeConfig($attributeCode, array $attributeConfig)
+    {
+        $product = $this->locator->getProduct();
+        $eavAttribute = $this->eavAttributeFactory->create()->loadByCode(Product::ENTITY, $attributeCode);
+
+        return array_replace_recursive($attributeConfig, [
+            'arguments' => [
+                'data' => [
+                    'config' => [
+                        'componentType' => 'dynamicRows',
+                        'formElement' => 'component',
+                        'renderDefaultRecord' => false,
+                        'itemTemplate' => 'record',
+                        'dataScope' => '',
+                        'dndConfig' => [
+                            'enabled' => false,
+                        ],
+                        'required' => (bool)$attributeConfig['arguments']['data']['config']['required'],
+                    ],
+                ],
+            ],
+            'children' => [
+                'record' => [
+                    'arguments' => [
+                        'data' => [
+                            'config' => [
+                                'componentType' => Container::NAME,
+                                'isTemplate' => true,
+                                'is_collection' => true,
+                                'component' => 'Magento_Ui/js/dynamic-rows/record',
+                                'dataScope' => '',
+                            ],
+                        ],
+                    ],
+                    'children' => [
+                        'value' => [
+                            'arguments' => [
+                                'data' => [
+                                    'config' => [
+                                        'componentType' => Field::NAME,
+                                        'formElement' => Input::NAME,
+                                        'dataType' => Text::NAME,
+                                        'label' => __('Value'),
+                                        'enableLabel' => true,
+                                        'dataScope' => 'value',
+                                        'validation' => [
+                                            'required-entry' => true
+                                        ],
+                                        'showLabel' => false,
+                                    ],
+                                ],
+                            ],
+                        ],
+                        'actionDelete' => [
+                            'arguments' => [
+                                'data' => [
+                                    'config' => [
+                                        'componentType' => 'actionDelete',
+                                        'dataType' => Text::NAME,
+                                        'label' => __('Action'),
+                                    ],
+                                ],
+                            ],
+                        ],
+                    ],
+                ],
+            ],
+        ]);
+    }   
+}
+```
+
+Then we set a custom backend model for every attribute of type `dynamicarray`.
+
+***`app/code/Goivvy/Attribute/etc/events.xml`***:
+
+```xml
+
+
+    
+        
+    
+
+```
+
+***`app/code/Goivvy/Attribute/Observer/UpdateElement.php`***:
+
+```php
+productType = $productType;
+        $this->productTypeConfig = $productTypeConfig;
+    }
+
+    public function execute(\Magento\Framework\Event\Observer $observer)
+    {
+        $backendModel = \Goivvy\Attribute\Model\Attribute\Backend\Dynamic::class;
+        $object = $observer->getEvent()->getAttribute();
+        if ($object->getFrontendInput() == 'dynamicarray') {
+            $object->setBackendModel($backendModel);
+            if (!$object->getApplyTo()) {
+                $applyTo = [];
+                foreach ($this->productType->getOptions() as $option) {
+                    if ($this->productTypeConfig->isProductSet($option['value'])) {
+                        continue;
+                    }
+                    $applyTo[] = $option['value'];
+                }
+                $object->setApplyTo($applyTo);
+            }
+        }
+
+        return $this;
+    }
+}
+```
+
+***`app/code/Goivvy/Attribute/Model/Attribute/Backend/Dynamic.php`***:
+
+```php
+_modelDynamic = $modelDynamic; 
+   }
+    
+   public function validate($object)
+   {
+      return $this; 
+   }
+     
+   public function afterLoad($object)
+   {
+      $data = $this->_modelDynamic->loadProductData($object, $this->getAttribute());
+
+      $object->setData($this->getAttribute()->getName(), $data);
+      return $this;
+   }
+
+   public function afterSave($object)
+   {
+        $orig = $object->getOrigData($this->getAttribute()->getName());
+        $current = $object->getData($this->getAttribute()->getName());
+        if ($orig == $current) {
+            return $this;
+        }
+
+        $this->_modelDynamic->deleteProductData($object, $this->getAttribute());
+        $values = $object->getData($this->getAttribute()->getName());
+
+        if (!is_array($values)) {
+            return $this;
+        }
+
+        foreach ($values as $value) {
+            if (empty($value['value'])  || !empty($value['delete'])) {
+                continue;
+            }
+
+            $data = [];
+            $data['value'] = $value['value'];
+            $data['attribute_id'] = $this->getAttribute()->getId();
+
+            $this->_modelDynamic->insertProductData($object, $data);
+        }
+
+        return $this;
+   }
+
+   public function afterDelete($object)
+   {
+        $this->_modelDynamic->deleteProductData($object, $this->getAttribute());
+        return $this;
+   }
+
+   public function getTable()
+   {
+        return $this->_modelDynamic->getTable('goivvy_dynamic');
+   }
+
+   public function getEntityIdField()
+   {
+        return $this->_modelDynamic->getIdFieldName();
+   }
+}
+```
+
+Then we setup a DB table to hold all values of product attributes of type `dynamicarray`.
+
+***`app/code/Goivvy/Attribute/etc/db_schema.xml`***:
+
+```xml
+
+
+    
+        
+        
+        
+        
+        
+            
+        
+        
+        
+        
+            
+        
+        
+            
+        
+    
+
+```
+
+And add a resource model for attributes of type `dynamicarray`.
+
+***`app/code/Goivvy/Attribute/Model/ResourceModel/Attribute/Dynamic.php`***:
+
+```php
+_init('goivvy_dynamic', 'id');
+    }
+
+    public function loadProductData($product, $attribute)
+    {
+        $select = $this->getConnection()->select()->from(
+            $this->getMainTable(),
+            ['value']
+        )->where(
+            'product_id = ?',
+            (int)$product->getId()
+        )->where(
+            'attribute_id = ?',
+            (int)$attribute->getId()
+        );
+        return $this->getConnection()->fetchAll($select);
+    }
+
+    public function deleteProductData($product, $attribute)
+    {
+        $where = ['product_id = ?' => (int)$product->getId(), 'attribute_id = ?' => (int)$attribute->getId()];
+
+        $connection = $this->getConnection();
+        $connection->delete($this->getMainTable(), $where);
+        return $this;
+    }
+
+    public function insertProductData($product, $data)
+    {
+        $data['product_id'] = (int)$product->getId();
+        $this->getConnection()->insert($this->getMainTable(), $data);
+        return $this;
+    }
+}
+```
+
+## Create a custom product attribute of type dynamicarray
+
+***`app/code/Goivvy/Attribute/Setup/InstallData.php`***:
+
+```php
+eavSetupFactory = $eavSetupFactory; 
+    }
+     
+    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
+    { 
+       $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);
+       $eavSetup->addAttribute(
+            \Magento\Catalog\Model\Product::ENTITY
+          , 'product_tags'
+          , [
+               'type' => 'static'
+             , 'backend' => 'Goivvy\Attribute\Model\Attribute\Backend\Dynamic'
+             , 'frontend' => ''
+             , 'label' => __('Product Tags')
+             , 'input' => 'dynamicarray'
+             , 'visible' => true
+             , 'visible_on_front' => true      
+             , 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL
+            ]
+        );
+    }
+}
+```
+
+## Display Product Tags attribute on frontend
+
+```php
+
+
+getProductTags() as $k => $v):?>
+
+
+
+
 
+```
+
+You can find a working copy of the extension on a [GitHub page](https://github.com/goivvy/new-attribute-type).