How to Create Dynamic Rows in Magento 2 Configuration

How to Create Dynamic Rows in Magento 2 Configuration

Dynamic rows in Magento 2 let you build flexible admin configuration panels where users add multiple data entries. This tutorial shows you how to implement dynamic rows in system configuration, giving store administrators control over repeatable data fields like category priorities, custom ranges, or product attributes.

What Are Dynamic Rows in Magento 2?

Dynamic rows are UI components that store data as collections. They give users the ability to add, edit, and delete multiple records directly in the admin panel. You'll find them useful for settings that need multiple values—shipping methods, price tiers, custom categories, or any repeatable configuration data.

The component supports various field types including text inputs, dropdowns, date pickers, and multiselect options. Data saves to the core_config_data table as serialized arrays.

Core Components Required

Component Purpose Location
system.xml Defines configuration structure etc/adminhtml/
Frontend Model Renders the dynamic row interface Block/Adminhtml/Form/Field/
Backend Model Handles data serialization Built-in Magento class
Custom Column Classes Creates dropdown or custom fields Block/Adminhtml/Form/Field/

Build the System Configuration File

Create system.xml in your module's etc/adminhtml directory. This file structures your configuration settings and tells Magento how to display them.

<?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>

<tab id="custom_settings" translate="label" sortOrder="100">

<label>Custom Settings</label>

</tab>

<section id="dynamic_configuration" translate="label" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">

<label>Dynamic Row Settings</label>

<tab>custom_settings</tab>

<resource>Vendor_Module::config</resource>

<group id="priority_settings" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">

<label>Category Priority Configuration</label>

<field id="priority_list" translate="label" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1">

<label>Priority Rules</label>

<frontend_model>Vendor\Module\Block\Adminhtml\Form\Field\Priority</frontend_model>

<backend_model>Magento\Config\Model\Config\Backend\Serialized\ArraySerialized</backend_model>

</field>

</group>

</section>

</system>

</config>

The frontend_model attribute points to your custom block that renders the interface. The backend_model handles data serialization automatically.

Create the Frontend Model Block

Build a block class that extends AbstractFieldArray. This class defines which columns appear in your dynamic row and manages the interface.

Create Priority.php in Block/Adminhtml/Form/Field/:

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!

Create the Frontend Model Block

Build a block class that extends AbstractFieldArray. This class defines which columns appear in your dynamic row and manages the interface.

Create Priority.php in Block/Adminhtml/Form/Field/:

<?php

namespace Vendor\Module\Block\Adminhtml\Form\Field;

use Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray;

use Magento\Framework\DataObject;

use Vendor\Module\Block\Adminhtml\Form\Field\Category;

class Priority extends AbstractFieldArray

{

private $categoryRenderer;

protected function _prepareToRender()

{

$this->addColumn('category', [

'label' => __('Category'),

'renderer' => $this->getCategoryRenderer()

]);

$this->addColumn('priority', [

'label' => __('Priority Value'),

'class' => 'required-entry validate-number'

]);

$this->addColumn('status', [

'label' => __('Status'),

'renderer' => $this->getCategoryRenderer()

]);

$this->_addAfter = false;

$this->_addButtonLabel = __('Add Priority Rule');

}

protected function _prepareArrayRow(DataObject $row)

{

$options = [];

$category = $row->getCategory();

if ($category !== null) {

$options['option_' . $this->getCategoryRenderer()->calcOptionHash($category)] = 'selected="selected"';

}

$row->setData('option_extra_attrs', $options);

}

private function getCategoryRenderer()

{

if (!$this->categoryRenderer) {

$this->categoryRenderer = $this->getLayout()->createBlock(

Category::class,

'',

['data' => ['is_render_to_js_template' => true]]

);

}

return $this->categoryRenderer;

}

}

Build Custom Column Renderers

For dropdown fields, create a custom renderer class. This example creates a category selector:

Create Category.php in Block/Adminhtml/Form/Field/:

<?php

namespace Vendor\Module\Block\Adminhtml\Form\Field;

use Magento\Framework\View\Element\Html\Select;

use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory;

use Magento\Framework\View\Element\Context;

class Category extends Select

{

protected $categoryCollection;

public function __construct(

CollectionFactory $collectionFactory,

Context $context,

array $data = []

) {

$this->categoryCollection = $collectionFactory;

parent::__construct($context, $data);

}

public function setInputName($value)

{

return $this->setName($value);

}

public function setInputId($value)

{

return $this->setId($value);

}

public function _toHtml()

{

if (!$this->getOptions()) {

$this->setOptions($this->getSourceOptions());

}

return parent::_toHtml();

}

private function getSourceOptions()

{

$options = [['label' => __('Select Category'), 'value' => 0]];

$categories = $this->categoryCollection

->create()

->addAttributeToSelect('name')

->addAttributeToSelect('entity_id');

foreach ($categories as $category) {

$options[] = [

'label' => $category->getName(),

'value' => $category->getId()

];

}

return $options;

}

}

Field Types You Can Add

Dynamic rows support multiple field types. Here are common implementations:

Text Input:

$this->addColumn('field_name', [

    'label' => __('Label'),

    'class' => 'required-entry'

]);

Number Input with Validation:

$this->addColumn('quantity', [

    'label' => __('Quantity'),

    'class' => 'required-entry validate-number validate-greater-than-zero'

]);

Dropdown/Select:

$this->addColumn('status', [

'label' => __('Status'),

'renderer' => $this->getStatusRenderer()

]);

Date Picker:

$this->addColumn('start_date', [

'label' => __('Start Date'),

'class' => 'js-date-excluded-datepicker'

]);

Access Configuration Values

Retrieve saved dynamic row data in your code using Magento's scope config:

<?php

namespace Vendor\Module\Helper;

use Magento\Framework\App\Helper\AbstractHelper;

use Magento\Framework\App\Helper\Context;

use Magento\Framework\Serialize\SerializerInterface;

class Data extends AbstractHelper

{

protected $serializer;

public function __construct(

Context $context,

SerializerInterface $serializer

) {

$this->serializer = $serializer;

parent::__construct($context);

}

public function getPriorityRules()

{

$configValue = $this->scopeConfig->getValue(

'dynamic_configuration/priority_settings/priority_list',

\Magento\Store\Model\ScopeInterface::SCOPE_STORE

);

if ($configValue) {

return $this->serializer->unserialize($configValue);

}

return [];

}

}

Common Validation Classes

Add these classes to your column definitions for data validation:

Validation Class Purpose
required-entry Makes field mandatory
validate-number Accepts numbers only
validate-email Validates email format
validate-url Checks for valid URLs
validate-greater-than-zero Positive numbers only
validate-digits Whole numbers without decimals
validate-alpha Alphabetic characters only

Testing Your Implementation

After creating your files, run these commands:

php bin/magento setup:upgrade

php bin/magento setup:di:compile

php bin/magento cache:clean

php bin/magento cache:flush

Navigate to Stores > Configuration > Custom Settings > Dynamic Row Settings in your admin panel. You should see your dynamic row interface with an "Add" button.

Advanced Features

Multiple Column Types in One Row:

You can mix different field types in a single dynamic row. Combine text inputs, dropdowns, and date pickers as needed.

Custom Button Labels:

$this->_addButtonLabel = __('Add New Rule');

Disable Row Sorting:

If you don't need drag-and-drop reordering:

$this->_addAfter = false;

Troubleshooting Common Issues

Data Not Saving: Verify your backend_model uses Magento\Config\Model\Config\Backend\Serialized\ArraySerialized.

Dropdown Not Showing Values: Check that your renderer class properly implements getSourceOptions() and returns an array of label-value pairs.

RJavaScript Errors: Clear your browser cache and Magento cache. Ensure is_render_to_js_template is set to true in your renderer.

Empty Values After Save: Add proper validation classes to required fields.

Performance Considerations

For large datasets in dropdowns, consider implementing lazy loading or filtering options. Cache category collections or other resource-intensive queries using Magento's cache system.

Dynamic rows work best for moderate amounts of data (under 100 rows). For larger datasets, consider using a custom grid with AJAX operations.

Conclusion

Dynamic rows give you flexibility in Magento 2 configuration. You create them by defining a system.xml file, building a frontend model block, and optionally adding custom column renderers for dropdowns or special fields. The backend model handles serialization automatically, and you access saved data through Magento's scope configuration system.

FAQs

What are dynamic rows in Magento 2 configuration?

Dynamic rows allow admins to add, edit, or remove multiple sets of related data directly from the Magento 2 configuration section. They’re often used for managing lists like shipping methods, payment mappings, or API credentials without hardcoding values.

Where are dynamic rows used in Magento 2?

Dynamic rows are used in the admin configuration forms (Stores > Configuration) under system.xml-defined sections, typically when a setting requires multiple entries such as IP lists, rate tables, or custom field mappings.

How do I create dynamic rows in Magento 2 configuration?

Follow these steps to create dynamic rows:

  • Define your configuration field in system.xml using the frontend_model type.
  • Create a block class extending Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray.
  • Add custom renderers for columns (e.g., dropdowns, text fields) by extending Magento\Framework\View\Element\Html\Select.
  • Implement your helper or model class to serialize and deserialize configuration values.

What is the role of AbstractFieldArray in dynamic rows?

The AbstractFieldArray class provides the base functionality for rendering dynamic rows. It handles adding, removing, and ordering rows while allowing developers to define custom columns through renderers.

How can I add custom column types in dynamic rows?

You can add custom columns by creating a renderer class that extends Magento\Framework\View\Element\Html\Select or Magento\Framework\View\Element\Html\Input. Then, in your field array block, add the renderer using the addColumn() method.

How do I save and retrieve dynamic row data?

Dynamic row data is usually serialized and stored as a string in the core_config_data table. Use Magento’s SerializerInterface to serialize before saving and deserialize when loading configuration values in your helper or model.

Can I validate input data in dynamic rows?

Yes, you can add validation by setting classes like required-entry, validate-number, or validate-email on your column inputs. This ensures data consistency before saving the configuration.

How do I display saved dynamic row data on the frontend?

Use a helper or model class to retrieve and deserialize the configuration value. Then loop through the resulting array in your frontend block or template to render the data as needed.

Are there best practices for implementing dynamic rows?

Yes, follow these guidelines:

  • Keep configuration data lightweight and relevant.
  • Use descriptive column names and tooltips for clarity.
  • Validate inputs properly before saving.
  • Serialize data securely and handle empty configurations gracefully.
  • Use dependency injection in helpers and avoid static calls.