[Fix It Series] Magento Error: Custom Option Type Fields Are Not Displayed Last Updated on 4 August 20254 August 2025 Mark Mac Magento Table of Contents Toggle PROBLEMSOLUTIONS PROBLEM Working on a module to add a product custom option input type (Derivated from file type). In the back-end, under catalog/product/custom options, the new option is present in the “input type” list but when selecting it, the associated custom option type fields are not displayed (e.g.: price, price type, sky, compatible file extension, etc.). app/code/A/Custoptiontype/etc/module.xml <?xml version="1.0" encoding="UTF-8"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="A_Custoptiontype" setup_version="1.0.0"> <sequence> <module name="Magento_Catalog"/> </sequence> </module> </config> app/code/A/Custoptiontype/etc/di.xml <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Options\Option" type="A\Custoptiontype\Block\Adminhtml\Product\Edit\Tab\Options\Option"/> <preference for="Magento\Catalog\Model\Product\Option" type="A\Custoptiontype\Model\Catalog\Product\Option"/> </config> app/code/A/Custoptiontype/etc/product_options.xml <?xml version="1.0" encoding="UTF-8"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/product_options.xsd"> <option name="xfile" label="X File" renderer="A\Custoptiontype\Block\Adminhtml\Product\Edit\Tab\Options\Type\Xfile"> <inputType name="xfile" label="X File" /> </option> </config> app/code/A/Custoptiontype/Block/Adminhtml/Product/Edit/Tab/Options/Option.php <?php namespace A\Custoptiontype\Block\Adminhtml\Product\Edit\Tab\Options; class Option extends \Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Options\Option { protected $_template = 'A_Custoptiontype::catalog/product/edit/options/option.phtml'; /** * Class constructor */ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Config\Model\Config\Source\Yesno $configYesNo, \Magento\Catalog\Model\Config\Source\Product\Options\Type $optionType, \Magento\Catalog\Model\Product $product, \Magento\Framework\Registry $registry, \Magento\Catalog\Model\ProductOptions\ConfigInterface $productOptionConfig, array $data = [] ) { parent::__construct( $context, $configYesNo, $optionType, $product, $registry, $productOptionConfig, $data ); } /** * Retrieve html templates for different types of product custom options * * @return string */ public function getTemplatesHtml() { $canEditPrice = $this->getCanEditPrice(); $canReadPrice = $this->getCanReadPrice(); $this->getChildBlock('xfile_option_type') ->setCanReadPrice($canReadPrice) ->setCanEditPrice($canEditPrice); $templates = parent::getTemplatesHtml() . "\n" . $this->getChildHtml('xfile_option_type'); return $templates; } } app/code/A/Custoptiontype/Block/Adminhtml/Product/Edit/Tab/Options/Type/Xfile.php <?php namespace A\Custoptiontype\Block\Adminhtml\Product\Edit\Tab\Options\Type; class Xfile extends \Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Options\Type\AbstractType { /** * @var string */ protected $_template = 'A_Custoptiontype::catalog/product/edit/options/type/xfile.phtml'; public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Catalog\Model\Config\Source\Product\Options\Price $optionPrice, array $data = [] ) { $this->_optionPrice = $optionPrice; parent::__construct($context, $optionPrice, $data); } } app/code/A/Custoptiontype/view/adminhtml/templates/catalog/product/edit/options/option.phtml <?php /** * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ // @codingStandardsIgnoreFile ?> <?php /** @var $block \A\Custoptiontype\Catalog\Block\Adminhtml\Product\Edit\Tab\Options\Option */ ?> <?php echo $block->getTemplatesHtml() ?> <script id="custom-option-base-template" type="text/x-magento-template"> <div class="fieldset-wrapper admin__collapsible-block-wrapper opened" id="option_<%- data.id %>"> <div class="fieldset-wrapper-title"> <strong class="admin__collapsible-title" data-toggle="collapse" data-target="#<%- data.id %>-content"> <span id="option_<%- data.id %>_header_title"><%- data.title %></span> </strong> <div class="actions"> <button type="button" title="<?php /* @escapeNotVerified */ echo __('Delete Custom Option'); ?>" class="action-delete" id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_delete"> <span><?php /* @escapeNotVerified */ echo __('Delete Custom Option'); ?></span> </button> </div> <div id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_move" data-role="draggable-handle" class="draggable-handle" title="<?php /* @escapeNotVerified */ echo __('Sort Custom Options'); ?>"></div> </div> <div class="fieldset-wrapper-content in collapse" id="<%- data.id %>-content"> <fieldset class="fieldset"> <fieldset class="fieldset-alt" id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>"> <input id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_is_delete" name="<?php /* @escapeNotVerified */ echo $block->getFieldName() ?>[<%- data.id %>][is_delete]" type="hidden" value=""/> <input id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_previous_type" name="<?php /* @escapeNotVerified */ echo $block->getFieldName() ?>[<%- data.id %>][previous_type]" type="hidden" value="<%- data.type %>"/> <input id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_previous_group" name="<?php /* @escapeNotVerified */ echo $block->getFieldName() ?>[<%- data.id %>][previous_group]" type="hidden" value=""/> <input id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_id" name="<?php /* @escapeNotVerified */ echo $block->getFieldName() ?>[<%- data.id %>][id]" type="hidden" value="<%- data.id %>"/> <input id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_option_id" name="<?php /* @escapeNotVerified */ echo $block->getFieldName() ?>[<%- data.id %>][option_id]" type="hidden" value="<%- data.option_id %>"/> <input name="<?php /* @escapeNotVerified */ echo $block->getFieldName() ?>[<%- data.id %>][sort_order]" type="hidden" value="<%- data.sort_order %>"/> <div class="field field-option-title required"> <label class="label" for="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_title"> <?php /* @escapeNotVerified */ echo __('Option Title') ?> </label> <div class="control"> <input id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_title" name="<?php /* @escapeNotVerified */ echo $block->getFieldName() ?>[<%- data.id %>][title]" class="required-entry input-text" type="text" value="<%- data.title %>" data-store-label="<%- data.title %>" <% if (typeof data.scopeTitleDisabled != 'undefined' && data.scopeTitleDisabled != null) { %> disabled="disabled" <% } %> > <%- data.checkboxScopeTitle %> </div> </div> <div class="field field-option-input-type required"> <label class="label" for="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_title"> <?php /* @escapeNotVerified */ echo __('Input Type') ?> </label> <div class="control opt-type"> <?php echo $block->getTypeSelectHtml() ?> </div> </div> <div class="field field-option-req"> <div class="control"> <input id="<?php /* @escapeNotVerified */ echo $block->getFieldId() ?>_<%- data.id %>_required" class="is-required" type="checkbox" checked="checked"/> <label for="field-option-req"> <?php /* @escapeNotVerified */ echo __('Required')?> </label> <span style="display:none"><?php echo $block->getRequireSelectHtml() ?></span> </div> </div> </fieldset> </fieldset> </div> </div> </script> <div id="import-container" style="display: none;"></div> <?php if (!$block->isReadonly()): ?> <div><input type="hidden" name="affect_product_custom_options" value="1"/></div> <?php endif; ?> <script> require([ "jquery", "Magento_Catalog/js/custom-options" ], function(jQuery){ jQuery(function ($) { var fieldSet = $('[data-block=product-custom-options]'); fieldSet.customOptions(<?php /* @escapeNotVerified */ echo $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode( [ 'fieldId' => $block->getFieldId(), 'productGridUrl' => $block->getProductGridUrl(), 'formKey' => $block->getFormKey(), 'customOptionsUrl' => $block->getCustomOptionsUrl(), 'isReadonly' => $block->isReadonly(), 'itemCount' => $block->getItemCount(), 'currentProductId' => $block->getCurrentProductId(), ] )?>); //adding data to templates <?php /** @var $_value \Magento\Framework\DataObject */ ?> <?php foreach ($block->getOptionValues() as $_value): ?> fieldSet.customOptions('addOption', <?php /* @escapeNotVerified */ echo $_value->toJson() ?>); <?php endforeach; ?> }); }); </script> app/code/A/Custoptiontype/view/adminhtml/templates/catalog/product/edit/options/type/xfile.phtml <?php /** * Copyright © 2016 Magento. All rights reserved. * See COPYING.txt for license details. */ // @codingStandardsIgnoreFile ?> <?php /** @var $block \A\Custoptiontype\Block\Adminhtml\Product\Edit\Tab\Options\Type\Xfile */ ?> <h1>TOTOTOTOTO</h1> <script id="custom-option-xfile-type-template" type="text/x-magento-template"> <div id="product_option_<%- data.option_id %>_type_<%- data.group %>" class="fieldset"> <table class="data-table"> <thead> <tr> <?php if ($block->getCanReadPrice() !== false) : ?> <th><?php /* @escapeNotVerified */ echo __('Price'); ?></th> <th><?php /* @escapeNotVerified */ echo __('Price Type'); ?></th> <?php endif; ?> <th><?php /* @escapeNotVerified */ echo __('SKU'); ?></th> <th><?php /* @escapeNotVerified */ echo __('Compatible File Extensions'); ?></th> <th><?php /* @escapeNotVerified */ echo __('Maximum Image Size'); ?></th> </tr> </thead> <tr> <?php if ($block->getCanReadPrice() !== false) : ?> <td class="opt-price"> <input name="product[options][<%- data.option_id %>][price]" data-store-label="<%- data.price %>" class="input-text validate-zero-or-greater" type="text" value="<%- data.price %>" <?php if ($block->getCanEditPrice() === false) : ?> disabled="disabled" <?php endif; ?>> </td> <td class="opt-price-type"><?php echo $block->getPriceTypeSelectHtml('data-attr="price-type"') ?><%- data.checkboxScopePrice %></td> <?php else : ?> <input name="product[options][<%- data.option_id %>][price]" type="hidden"> <input id="product_option_<%- data.option_id %>_price_type" name="product[options][<%- data.option_id %>][price_type]" type="hidden"> <?php endif; ?> <td> <input name="product[options][<%- data.option_id %>][sku]" class="input-text" type="text" value="<%- data.sku %>"> </td> <td> <input name="product[options][<%- data.option_id %>][file_extension]" class="input-text" type="text" value="<%- data.file_extension %>"> </td> <td class="col-file"><?php /* @escapeNotVerified */ echo __('%1 <span>x</span> %2 <span>px.</span>', '<input class="input-text" type="text" name="product[options][<%- data.option_id %>][image_size_x]" value="<%- data.image_size_x %>">', '<input class="input-text" type="text" name="product[options][<%- data.option_id %>][image_size_y]" value="<%- data.image_size_y %>">') ?> <div class="note"><?php /* @escapeNotVerified */ echo __('Please leave blank if it is not an image.') ?></div> </td> </tr> </table> </div> </script> SOLUTIONS Rename namepsace from Custoptiontype to CustomOptions Step 1: Add folder Ui Create the file CustomOption.php in A\CustomOption\Ui\DataProvider\Catalog\Product\Form\Modifier Code CustomOption.php <?php namespace A\CustomOptions\Ui\DataProvider\Catalog\Product\Form\Modifier; use Magento\Catalog\Model\Locator\LocatorInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Catalog\Model\ProductOptions\ConfigInterface; use Magento\Catalog\Model\Config\Source\Product\Options\Price as ProductOptionsPrice; use Magento\Framework\UrlInterface; use Magento\Framework\Stdlib\ArrayManager; use Magento\Ui\Component\Modal; use Magento\Ui\Component\Container; use Magento\Ui\Component\DynamicRows; use Magento\Ui\Component\Form\Fieldset; use Magento\Ui\Component\Form\Field; use Magento\Ui\Component\Form\Element\Input; use Magento\Ui\Component\Form\Element\Select; use Magento\Ui\Component\Form\Element\Checkbox; use Magento\Ui\Component\Form\Element\ActionDelete; use Magento\Ui\Component\Form\Element\DataType\Text; use Magento\Ui\Component\Form\Element\DataType\Number; use Magento\Framework\Locale\CurrencyInterface; class CustomOptions extends \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\CustomOptions { const FIELD_TESTOPTION_NAME = 'testoption'; protected function getStaticTypeContainerConfig($sortOrder) { return [ 'arguments' => [ 'data' => [ 'config' => [ 'componentType' => Container::NAME, 'formElement' => Container::NAME, 'component' => 'Magento_Ui/js/form/components/group', 'breakLine' => false, 'showLabel' => false, 'additionalClasses' => 'admin__field-group-columns admin__control-group-equal', 'sortOrder' => $sortOrder, ], ], ], 'children' => [ static::FIELD_PRICE_NAME => $this->getPriceFieldConfig(10), static::FIELD_PRICE_TYPE_NAME => $this->getPriceTypeFieldConfig(20), static::FIELD_SKU_NAME => $this->getSkuFieldConfig(30), static::FIELD_TESTOPTION_NAME => $this->getTestoptionFieldConfig(30), static::FIELD_MAX_CHARACTERS_NAME => $this->getMaxCharactersFieldConfig(40), static::FIELD_FILE_EXTENSION_NAME => $this->getFileExtensionFieldConfig(50), static::FIELD_IMAGE_SIZE_X_NAME => $this->getImageSizeXFieldConfig(60), static::FIELD_IMAGE_SIZE_Y_NAME => $this->getImageSizeYFieldConfig(70) ] ]; } /** * Get config for grid for "select" types * * @param int $sortOrder * @return array */ protected function getSelectTypeGridConfig($sortOrder) { return [ 'arguments' => [ 'data' => [ 'config' => [ 'addButtonLabel' => __('Add Value'), 'componentType' => DynamicRows::NAME, 'component' => 'Magento_Ui/js/dynamic-rows/dynamic-rows', 'additionalClasses' => 'admin__field-wide', 'deleteProperty' => static::FIELD_IS_DELETE, 'deleteValue' => '1', 'renderDefaultRecord' => false, 'sortOrder' => $sortOrder, ], ], ], 'children' => [ 'record' => [ 'arguments' => [ 'data' => [ 'config' => [ 'componentType' => Container::NAME, 'component' => 'Magento_Ui/js/dynamic-rows/record', 'positionProvider' => static::FIELD_SORT_ORDER_NAME, 'isTemplate' => true, 'is_collection' => true, ], ], ], 'children' => [ static::FIELD_TITLE_NAME => $this->getTitleFieldConfig(10), static::FIELD_PRICE_NAME => $this->getPriceFieldConfig(20), static::FIELD_PRICE_TYPE_NAME => $this->getPriceTypeFieldConfig(30, ['fit' => true]), static::FIELD_SKU_NAME => $this->getSkuFieldConfig(40), static::FIELD_SORT_ORDER_NAME => $this->getPositionFieldConfig(50), static::FIELD_IS_DELETE => $this->getIsDeleteFieldConfig(60) ] ] ] ]; } protected function getTypeFieldConfig($sortOrder) { return [ 'arguments' => [ 'data' => [ 'config' => [ 'label' => __('Option Type'), 'componentType' => Field::NAME, 'formElement' => Select::NAME, 'component' => 'Magento_Catalog/js/custom-options-type', 'elementTmpl' => 'ui/grid/filters/elements/ui-select', 'selectType' => 'optgroup', 'dataScope' => static::FIELD_TYPE_NAME, 'dataType' => Text::NAME, 'sortOrder' => $sortOrder, 'options' => $this->getProductOptionTypes(), 'disableLabel' => true, 'multiple' => false, 'selectedPlaceholders' => [ 'defaultPlaceholder' => __('-- Please select --'), ], 'validation' => [ 'required-entry' => true ], 'groupsConfig' => [ 'text' => [ 'values' => ['field', 'area'], 'indexes' => [ static::CONTAINER_TYPE_STATIC_NAME, static::FIELD_PRICE_NAME, static::FIELD_PRICE_TYPE_NAME, static::FIELD_SKU_NAME, static::FIELD_MAX_CHARACTERS_NAME ] ], 'Testvendor' => [ 'values' => ['testoption'], 'indexes' => [ static::CONTAINER_TYPE_STATIC_NAME, #static::FIELD_SKU_NAME, static::FIELD_TESTOPTION_NAME, ] ], 'file' => [ 'values' => ['file'], 'indexes' => [ static::CONTAINER_TYPE_STATIC_NAME, static::FIELD_PRICE_NAME, static::FIELD_PRICE_TYPE_NAME, static::FIELD_SKU_NAME, static::FIELD_FILE_EXTENSION_NAME, static::FIELD_IMAGE_SIZE_X_NAME, static::FIELD_IMAGE_SIZE_Y_NAME ] ], 'select' => [ 'values' => ['drop_down', 'radio', 'checkbox', 'multiple'], 'indexes' => [ static::GRID_TYPE_SELECT_NAME ] ], 'data' => [ 'values' => ['date', 'date_time', 'time'], 'indexes' => [ static::CONTAINER_TYPE_STATIC_NAME, static::FIELD_PRICE_NAME, static::FIELD_PRICE_TYPE_NAME, static::FIELD_SKU_NAME ] ] ], ], ], ], ]; } protected function getTestoptionFieldConfig($sortOrder) { return [ 'arguments' => [ 'data' => [ 'config' => [ 'label' => __('Test Option'), 'componentType' => Field::NAME, 'formElement' => Input::NAME, 'dataScope' => static::FIELD_SKU_NAME, 'dataType' => Text::NAME, 'sortOrder' => $sortOrder, ], ], ], ]; } } Step 2: Edit: product_options.xml app/code/A/ CustomOptions /etc/product_options.xml <?xml version="1.0" encoding="UTF-8"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Catalog:etc/product_options.xsd"> <option name="xfile" label="X File" renderer="A\CustomOptions\Block\Adminhtml\Product\Edit\Tab\Options\Type\Xfile"> <inputType name="testoption" label="Test Option" /> </option> </config> After that, you run the following command in the bin director: ./magento setup:upgrade ./magento setup:di:compile ./magento cache:flush ./magento cache:clean We have shown you 2 simple steps to fix the error: Custom Option Type Fields Are Not Displayed in Magento 2. If you have any problems when following this instruction, be free to leave a comment below. More From Magento Fix It Series [Fix It Series] Front Controller Reached 100 router Match Iterations [ratings] Mark Mac Mark Mac is our adept Project Lead at Tigren. A skilled problem solver and expert developer, Mark's leadership in web/app projects is evident through his strategic thinking and technical prowess. Committed to propelling businesses forward in the dynamic tech landscape, Mark ensures excellence in every endeavor. Share Table of Contents Toggle PROBLEMSOLUTIONS