Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Magento 2 Custom Attribute Development

By Kokil Thapa | Last reviewed: September 2026

Magento 2 custom attribute development is how you store business-specific product, category, customer, and order data that the default schema does not cover. Adobe Commerce and Magento Open Source use an Entity–Attribute–Value (EAV) model, so attributes are not simple columns on one table. They are typed fields with backend, frontend, and source behaviour attached. On real eCommerce builds—florist shops with stem-length filters, B2B catalogs with MOQ fields, or Nepal stores with bilingual labels—custom attributes carry the data that drives search, checkout rules, and admin workflows. This guide walks through the patterns I use on production eCommerce development projects running Magento 2.4.x on PHP 8.2 or higher.

What Is Magento 2 Custom Attribute Development and Why Does EAV Matter?

Every catalog entity in Magento 2—product, category, customer, customer address—stores flexible data through EAV tables. A product row in catalog_product_entity holds identity fields. Attribute values live in type-specific tables such as catalog_product_entity_varchar or catalog_product_entity_int.

That design lets merchants add hundreds of attributes without altering core tables. It also means you must think in attribute metadata, not column names. When you create a custom attribute, you define:

  • Attribute code — snake_case identifier, immutable after creation
  • Backend type — varchar, int, decimal, text, datetime
  • Frontend input — text, textarea, select, multiselect, boolean, date, media_image
  • Scope — global, website, or store view
  • Behaviour flags — required, searchable, filterable, comparable, visible on front

If you are new to module structure, read the companion piece on Magento 2 custom module development for beginners first. Custom attributes almost always ship inside a module.

Magento 2 EAV ArchitectureEntitycatalog_product_entityAttributeeav_attributeValue Tablesvarchar / int / textCustom Attribute MetadataSource ModelBackend ModelFrontend InputScopeSetup Patch registers all metadata at deploy timeReindex + cache flush required after changes
Magento 2 custom attribute development maps entities to typed EAV value tables through shared attribute metadata

How Do You Create Custom Attributes Programmatically in Magento 2?

Programmatic creation is the right default for team projects. Admin-created attributes are hard to track in Git. They drift between environments. Setup Patches (introduced in Magento 2.3) replace the old InstallSchema/UpgradeSchema pattern for attribute work.

Step 1: Scaffold the module

Create a module under app/code/Vendor/Module with a standard registration.php and etc/module.xml. Pin your module sequence after Magento_Catalog when adding product attributes.

Step 2: Write a Setup Patch

A Setup Patch runs once and is tracked in the patch_list table. Use EavSetup or the ProductAttributeManagementInterface for cleaner typing on newer codebases.

<?php
declare(strict_types=1);

namespace Vendor\CustomAttributes\Setup\Patch\Data;

use Magento\Catalog\Model\Product;
use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;

class AddStemLengthAttribute implements DataPatchInterface
{
    public function __construct(
        private ModuleDataSetupInterface $moduleDataSetup,
        private EavSetupFactory $eavSetupFactory
    ) {}

    public function apply(): void
    {
        $this->moduleDataSetup->getConnection()->startSetup();
        $eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);

        $eavSetup->addAttribute(
            Product::ENTITY,
            'stem_length_cm',
            [
                'type' => 'int',
                'label' => 'Stem Length (cm)',
                'input' => 'select',
                'source' => \Vendor\CustomAttributes\Model\Product\Attribute\Source\StemLength::class,
                'required' => false,
                'global' => ScopedAttributeInterface::SCOPE_GLOBAL,
                'visible' => true,
                'user_defined' => true,
                'searchable' => true,
                'filterable' => true,
                'comparable' => false,
                'visible_on_front' => true,
                'used_in_product_listing' => true,
                'is_html_allowed_on_front' => false,
                'group' => 'General',
                'sort_order' => 50,
            ]
        );

        $this->moduleDataSetup->getConnection()->endSetup();
    }

    public static function getDependencies(): array
    {
        return [];
    }

    public function getAliases(): array
    {
        return [];
    }
}

Step 3: Deploy and verify

  1. Run bin/magento setup:upgrade on staging first
  2. Run bin/magento cache:flush
  3. Reindex if the attribute is filterable: bin/magento indexer:reindex catalogsearch_fulltext
  4. Confirm the attribute under Stores → Attributes → Product in admin

I've seen production failures when developers skip reindex after adding filterable attributes. Layered navigation stays empty until the catalog search index picks up the new field. For large catalogs, plan this during a maintenance window and review Magento 2 performance optimization practices before bulk attribute changes.

Which Attribute Input Types Should You Choose for Magento 2 Custom Attribute Development?

Picking the wrong backend type is a common mistake. You cannot freely change type after data exists. Plan upfront.

Business needBackend typeFrontend inputNotes
Short label, SKU suffix, badge textvarchartext255 char limit; good for single-line strings
Long description block, care instructionstexttextarea or Page BuilderStored in text table; watch WYSIWYG scope
Fixed option set (size, colour family)intselect or multiselectRequires source model or option array
Yes/no flagintbooleanStores 0 or 1
Price adjustment, weight factordecimaltext with validationUse validation class for numeric input
Launch date, expirydatetimedateTimezone-aware; test per store view

Multiselect attributes store comma-separated option IDs in varchar tables. Custom source models must return option arrays in the format Magento expects. Validate your JSON option payloads with a JSON formatter during API integration work.

Source models for select and multiselect

A source model implements \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource and returns getAllOptions(). Keep options translatable when you run multi-store setups.

<?php
declare(strict_types=1);

namespace Vendor\CustomAttributes\Model\Product\Attribute\Source;

use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource;

class StemLength extends AbstractSource
{
    public function getAllOptions(): array
    {
        if ($this->_options === null) {
            $this->_options = [
                ['value' => '', 'label' => __('-- Please Select --')],
                ['value' => '40', 'label' => __('40 cm')],
                ['value' => '50', 'label' => __('50 cm')],
                ['value' => '60', 'label' => __('60 cm')],
            ];
        }
        return $this->_options;
    }
}

For dynamic options loaded from an external ERP, consider a custom backend model that syncs options via cron instead of hard-coding arrays. That pattern appears often on international florist eCommerce builds where stem lengths vary by supplier.

Custom Attribute Deploy FlowData Patchin Gitsetup:upgradepatch_listeav_attributemetadata rowAdmin + APIreadyPost-Deploy Checklistcache:flushindexer:reindexAssign attribute setMissing attribute set assignment = invisible on product edit form
Deploy pipeline for Magento 2 custom attribute development from Setup Patch through cache flush and reindex

How Do You Display Custom Attributes on the Magento 2 Storefront?

Creating the attribute is half the job. Storefront display depends on theme layout, attribute flags, and sometimes custom templates.

Product detail page

Set visible_on_front to true during attribute creation. In Luma-based themes, attributes appear in the "More Information" tab when flagged correctly. Hyvä and PWA frontends often need explicit GraphQL or REST exposure.

For headless builds, confirm the attribute appears in the Magento 2 GraphQL API product query. Custom attributes on products are typically available under custom_attributes when included in the attribute set. Category and customer attributes follow similar patterns with different entity types.

Category and customer attributes

Category attributes use \Magento\Catalog\Model\Category as the entity. Customer attributes go through CustomerSetup or the customer attribute repository. I've used customer attributes on B2B portals for PAN/VAT fields where Nepal businesses need invoicing metadata alongside standard address data.

Layout XML override example

When you need attributes in a custom block—not the default tab—reference them in a template via the product model:

<?php
/** @var \Magento\Catalog\Model\Product $product */
$stemLength = $product->getData('stem_length_cm');
$label = $product->getResource()
    ->getAttribute('stem_length_cm')
    ->getFrontend()
    ->getValue($product);
?>
<dl class="product-attribute stem-length">
    <dt><?= $escaper->escapeHtml(__('Stem Length')) ?></dt>
    <dd><?= $escaper->escapeHtml($label) ?></dd>
</dl>

Always escape output. Custom attributes often carry user-supplied text from admin imports. Treat them like any other untrusted string at render time.

What Is the Difference Between Custom Attributes and Extension Attributes in Magento 2?

Teams confuse these two extension points. They solve different problems.

CriteriaCustom EAV attributeExtension attribute
StorageEAV value tables (or flat when indexed)Custom DB table or computed at runtime
Admin UINative attribute formsRequires custom UI or plugins
Search / layered navSupported when flagged filterableNot automatic; custom indexer needed
API exposureVia custom_attributes arrayDeclared in extension_attributes.xml
Best forMerchant-editable catalog fieldsIntegration payloads, computed values

Use EAV custom attributes when merchandisers must edit values in admin and those values belong on the product long term. Use extension attributes when you attach shipment-bridge data, ERP sync tokens, or API-only structures that should not appear in standard attribute grids.

The official Adobe Commerce documentation on adding product attributes programmatically aligns with the Setup Patch approach described here. For API-only fields, review the extension attributes guide on Adobe's developer portal.

EAV Attribute vs Extension AttributeCustom EAV AttributeAdmin editableFilterable in layered navStored in EAV tablesSetup Patch deployExtension AttributeAPI / service layerCustom table optionalPlugin or join loaderNot in admin gridChoose EAV for merchandising data; extension attributes for integrations
Decision split for Magento 2 custom attribute development: merchant-facing EAV fields vs integration extension attributes

How Do You Make Custom Attributes Searchable and Filterable in Magento 2?

Searchable attributes feed full-text search and Elasticsearch/OpenSearch indexes on Magento 2.4.x. Filterable attributes appear in layered navigation on category pages.

Indexer and Elasticsearch interaction

When filterable is set with option "Filterable (with results)" or "Filterable (no results)", Magento includes the attribute in catalog search indexing. After bulk attribute rollout, run a full reindex—not partial—on staging and compare query results against production expectations.

Large catalogs benefit from the tuning notes in Magento 2 Elasticsearch setup and tuning. Wrong field mappings can make numeric attributes behave like text tokens. That breaks range filters.

Attribute set assignment

An attribute that is not assigned to the product's attribute set never appears on the edit form. New attributes default to all sets only when you specify that during creation or run a follow-up patch.

$entityTypeId = $eavSetup->getEntityTypeId(Product::ENTITY);
$attributeSetIds = $eavSetup->getAllAttributeSetIds($entityTypeId);

foreach ($attributeSetIds as $attributeSetId) {
    $groupId = $eavSetup->getAttributeGroupId($entityTypeId, $attributeSetId, 'General');
    $eavSetup->addAttributeToGroup(
        $entityTypeId,
        $attributeSetId,
        $groupId,
        'stem_length_cm',
        50
    );
}

Import and CSV workflows

Custom attribute columns import like native fields when headers match attribute codes. For large feeds, follow Magento 2 import products from CSV at scale guidance. Validate option IDs before import. A typo in a select value silently creates empty storefront output.

Multi-store setups add scope complexity. A global-scoped attribute shares one value across stores. Website-scoped attributes vary per website—useful when you run NPR pricing metadata on a Nepal website and USD metadata elsewhere within one Magento instance. Review Magento 2 multi-store configuration before locking scope decisions.

Filterable Attribute Index PathProduct EAVcustom valuesCatalog Indexmview / cronOpenSearchfield mappingLayered NavfiltersCommon Filter FailuresNot filterableflag missingStale indexafter deployWrong scopeper store viewRun catalogsearch_fulltext reindex after attribute flag changes
Filterable Magento 2 custom attributes flow from EAV storage through catalog index into OpenSearch layered navigation

What Production Gotchas Should You Avoid in Magento 2 Custom Attribute Development?

After years of maintaining Magento 2.4.x stores, a few failures repeat across projects.

Renaming or deleting attribute codes

Attribute codes are effectively permanent. Renaming breaks imports, theme references, and third-party modules. Deprecate old codes with a migration patch that copies values to a new attribute instead of renaming in place.

Too many filterable attributes

Each filterable field adds index weight and UI clutter. Merchandising teams often request fifteen filters. Engineering should push back and keep filters tied to conversion data. Performance work belongs in the same conversation—see testing and optimization services for load testing layered navigation under realistic traffic.

Flat catalog and legacy modes

Most Magento 2.4.x installs rely on Elasticsearch rather than MySQL flat catalog. Do not assume flat indexer settings from Magento 1 carry forward. Confirm indexer modes with bin/magento indexer:status after every deploy.

Module conflicts

Third-party modules sometimes register attributes with generic codes like custom_field_1. Namespace your codes with a vendor prefix: vendor_stem_length prevents collisions. Before choosing Magento over alternatives, the platform comparison in Magento 2 vs Shopify vs WooCommerce helps frame when deep attribute modelling is worth the EAV complexity.

Security and validation

Attributes marked is_html_allowed_on_front can introduce XSS if admins paste untrusted HTML. Disable HTML on front for text fields unless you truly need it. Use backend models to sanitize on save when attributes accept structured input.

For REST and headless consumers, confirm read/write behaviour through the Magento 2 REST API for headless storefronts. Writable custom attributes need explicit ACL awareness. Do not expose internal integration codes to public tokens.

When estimating project scope for Nepal clients, attribute modelling often adds 8–20 hours depending on count, filter rules, and import mappings. That sits alongside broader build costs covered in Nepal eCommerce website development cost guides. Budget for QA across attribute sets, not just the default one.

Key Takeaways

  • Ship custom product attributes via Data Setup Patches in Git—avoid admin-only creation on multi-environment projects.
  • Match backend type and frontend input to the data shape upfront; changing type after values exist is painful.
  • Assign attributes to every relevant attribute set and reindex catalog search when filterable flags are enabled.
  • Use EAV custom attributes for merchant-editable catalog data; use extension attributes for integration-only payloads.
  • Namespace attribute codes, escape storefront output, and test imports with real option IDs before production feeds.
  • Plan cache flush, reindex, and staging verification as part of every deploy—not as optional cleanup.

People Also Ask

Can you create Magento 2 custom attributes without a module?

Yes. Admin users can create attributes under Stores → Attributes → Product. That works for solo merchants on one environment. For teams running staging and production, programmatic Setup Patches keep attributes versioned, reviewable, and repeatable across deploys.

How do you update an existing custom attribute in Magento 2?

Write a new Data Patch that loads the attribute by code and updates metadata through EavSetup::updateAttribute(). You cannot change backend type safely if values already exist. For label or flag changes, updateAttribute is sufficient. Always flush cache and reindex when search or filter flags change.

Are custom attributes available in Magento 2 GraphQL and REST APIs?

Product custom attributes appear in REST and GraphQL product payloads when included in the attribute set and not restricted by ACL. Category and customer custom attributes require entity-specific endpoints. Extension attributes need separate declaration in extension_attributes.xml and loader plugins.

What PHP version does Magento 2.4.x need for custom attribute modules?

Magento 2.4.x supports PHP 8.2 and 8.3 on current releases; confirm against your exact Adobe Commerce or Open Source release notes before deploying. Use declare(strict_types=1); in new patches and run bin/magento setup:di:compile in production mode after module installation.

Ship Custom Attributes With Confidence

Magento 2 custom attribute development is foundational work—not a side task. Attributes drive filters, PDP content, import columns, and API contracts. Treat them like schema migrations: patch-based, reviewed, indexed, and tested on every attribute set you sell through.

If you are planning a new catalog, migrating from another platform, or untangling attribute debt on a live store, structured attribute design saves months of rework. Review related work on Magento 2 payment gateway integration and ongoing support and maintenance, or contact us to discuss your Magento 2.4.x build. You can also browse the project portfolio and read more on the blog for platform-specific guides.

Frequently Asked Questions

It adds typed EAV fields for product, category, customer, and order data outside the default schema, stored in tables like catalog_product_entity_varchar rather than core entity columns.

No. Codes are permanent after creation. Renaming breaks imports, themes, and modules. Copy values to a new attribute via a migration patch instead.

Run bin/magento setup:upgrade, bin/magento cache:flush, then bin/magento indexer:reindex catalogsearch_fulltext. Skipping reindex leaves layered navigation empty until the search index updates.

Every catalog entity stores flexible data through EAV tables. A product row in catalog_product_entity holds identity fields while values live in type-specific tables such as catalog_product_entity_varchar or catalog_product_entity_int. That design lets merchants add hundreds of attributes without altering core tables. It also means you must think in attribute metadata—code, backend type, frontend input, scope, and behaviour flags—not simple column names when planning fields for search, checkout rules, and admin workflows.

Scaffold a module under app/code/Vendor/Module with registration.php and etc/module.xml, sequencing after Magento_Catalog for product attributes. Write a Setup Patch implementing DataPatchInterface that uses EavSetupFactory to call addAttribute with type, label, input, source, scope, and visibility flags. Setup Patches run once and are tracked in patch_list. Deploy on staging first with bin/magento setup:upgrade, then cache:flush. Confirm the attribute under Stores → Attributes → Product in admin before promoting to production.

Programmatic creation is the right default for team projects. Admin-created attributes are hard to track in Git and drift between environments. Setup Patches, introduced in Magento 2.3, replace the old InstallSchema and UpgradeSchema pattern for attribute work and give you version-controlled, repeatable deployments. Reserve the admin UI for quick experiments or merchant-owned fields you do not need synchronized across staging and production through your module codebase.

Match backend type to business need and plan upfront—you cannot freely change type after data exists. Use varchar with text for short labels under 255 characters, text with textarea for long blocks, int with select or multiselect for fixed option sets requiring a source model, int boolean for yes or no flags, decimal for numeric price or weight factors, and datetime for launch or expiry dates. Multiselect attributes store comma-separated option IDs in varchar tables, so validate option payloads carefully during API and import work.

Custom EAV attributes store values in EAV value tables, appear in native admin attribute forms, support search and layered navigation when flagged filterable, and expose through the custom_attributes array in APIs. Extension attributes live in custom DB tables or are computed at runtime, require custom UI or plugins, need custom indexers for search, and are declared in extension_attributes.xml. Use EAV when merchandisers must edit catalog fields long term. Use extension attributes for ERP sync tokens, shipment-bridge data, or API-only structures that should not appear in standard attribute grids.

Set searchable to true so the attribute feeds full-text search and Elasticsearch or OpenSearch indexes on Magento 2.4.x. Set filterable to Filterable with results or Filterable no results for layered navigation on category pages. After bulk attribute rollout, run a full reindex on staging—not partial—and compare query results. Assign the attribute to product attribute sets with addAttributeToGroup or it never appears on edit forms. Wrong Elasticsearch field mappings can make numeric attributes behave like text tokens and break range filters.

Set visible_on_front to true during attribute creation. On Luma-based themes, correctly flagged attributes appear in the More Information tab. For custom placement outside default tabs, reference values in templates via getData and getFrontend getValue, always escaping output because admin imports may carry untrusted text. Hyvä and PWA frontends often need explicit GraphQL or REST exposure—confirm the attribute appears under custom_attributes in product queries when it is included in the attribute set.

Scope controls where attribute values can differ across your install. Global scope shares one value across all websites and store views. Website scope allows different values per website, useful when one Magento instance serves NPR pricing metadata on a Nepal website and USD metadata elsewhere. Store view scope varies per storefront language or branding layer. Lock scope decisions early during Setup Patch design because changing scope after merchants enter data creates migration headaches and inconsistent catalog behaviour.

Custom attribute columns import like native fields when CSV headers match attribute codes exactly, not admin labels. For select and multiselect attributes, validate option IDs before import—a typo in a select value silently creates empty storefront output. Large feeds should follow Magento import-at-scale guidance to avoid timeouts and partial failures. Because attribute codes are immutable after creation, keep your import templates aligned with the snake_case codes defined in Setup Patches across every environment.

Never rename attribute codes in place; deprecate old codes with a migration patch instead. Namespace codes with a vendor prefix like vendor_stem_length to avoid collisions with third-party modules registering generic codes such as custom_field_1. Limit filterable attributes because each adds index weight and layered navigation clutter. On Magento 2.4.x, confirm indexer modes with bin/magento indexer:status rather than assuming legacy flat catalog settings from Magento 1. Disable is_html_allowed_on_front on text fields unless truly needed to reduce XSS risk from admin-pasted HTML.

Select and multiselect attributes require a source model implementing Magento Eav Model Entity Attribute Source AbstractSource with getAllOptions returning value and label pairs in the format Magento expects. Hard-code options for fixed sets such as stem lengths, keeping labels translatable for multi-store setups. For dynamic options loaded from an external ERP, consider a custom backend model that syncs options via cron instead of static arrays—a pattern I have used on international florist eCommerce builds where stem lengths vary by supplier and change frequently.

Product attributes use Product ENTITY with EavSetup inside a module sequenced after Magento_Catalog. Category attributes target Magento Catalog Model Category as the entity type with the same EavSetup addAttribute patterns. Customer attributes go through CustomerSetup or the customer attribute repository. On B2B portals I have used customer attributes for PAN or VAT invoicing metadata alongside standard address data where Nepal businesses need tax identifiers stored on customer accounts rather than only on individual orders or invoices.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: