In this tutorial, we will explore the process of creating a fully functional Admin Grid in Magento 2. Admin Grids are commonly used to display and manage data in a tabular format, making them an essential component of any custom Magento module. This guide will walk you through each step of the process, from setting up the necessary files to rendering the grid in the Magento Admin.

Prerequisites

Before you begin, make sure you have the following:


Step 1: Create the Module Structure

First, create a new module to house your Admin Grid functionality. For this tutorial, we’ll name the module Vendor_Grid.

  1. Navigate to the app/code directory of your Magento installation and create the necessary folders:
app/code/Vendor/Grid
  1. Inside the Grid folder, create the following subdirectories:
app/code/Vendor/Grid/
├── registration.php
├── etc/
│   └── module.xml
├── Controller/
│   └── Adminhtml/
│       └── Post/
│           └── Index.php
├── Model/
│   ├── Post.php
│   └── ResourceModel/
│       └── Post.php
│       └── Collection.php
├── view/
│   └── adminhtml/
│       ├── layout/
│       │   └── vendor_grid_index.xml
│       └── ui_component/
│           └── vendor_grid_listing.xml

Step 2: Register the Module

Create a registration.php file in the Vendor/Grid directory to register the module with Magento.

<?php

use Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(
    ComponentRegistrar::MODULE,
    'Vendor_Grid',
    __DIR__
);

Step 3: Declare the Module in module.xml

Create a module.xml file in the etc directory to declare the module.

<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Vendor_Grid"/>
</config>

Step 4: Set Up the Database Schema

To store the grid data, you’ll need a custom database table. Create a db_schema.xml file in the etc directory.

<?xml version="1.0" ?>
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
    <table name="vendor_grid_post" resource="default" engine="innodb" comment="Post Table">
        <column xsi:type="int" name="post_id" padding="10" unsigned="true" nullable="false" identity="true"
                comment="Entity Id"/>
        <constraint xsi:type="primary" referenceId="PRIMARY">
            <column name="post_id"/>
        </constraint>
        <column name="title" nullable="false" xsi:type="varchar" comment="Title" length="255"/>
        <column name="content" nullable="false" xsi:type="text" comment="Content"/>
        <column name="created_at" nullable="false" xsi:type="timestamp" comment="Creation Time"
                default="CURRENT_TIMESTAMP"/>
    </table>
</schema>

Step 5: Create the Model, Resource Model, and Collection

Next, create the Model and ResourceModel classes to handle data interactions.

Model

Create the Model/Post.php file.

<?php

declare(strict_types=1);

namespace Vendor\Grid\Model;

use Magento\Framework\Model\AbstractModel;

class Post extends AbstractModel
{
    /**
     * Model construct that should be used for object initialization
     *
     * @return void
     */
    public function _construct(): void
    {
        $this->_init(ResourceModel\Post::class);
    }
}

ResourceModel

Create the Model/ResourceModel/Post.php file.

<?php

declare(strict_types=1);

namespace Vendor\Grid\Model\ResourceModel;

use Magento\Framework\Model\ResourceModel\Db\AbstractDb;

class Post extends AbstractDb
{
    /**
     * Resource initialization
     *
     * @return void
     */
    protected function _construct(): void
    {
        $this->_init('vendor_grid_post', 'post_id');
    }
}

Collection

Create the Model/ResourceModel/Post/Collection.php file.

<?php

declare(strict_types=1);

namespace Vendor\Grid\Model\ResourceModel\Post;

use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
use Vendor\Grid\Model\Post;
use Vendor\Grid\Model\ResourceModel\Post as PostResource;

class Collection extends AbstractCollection
{
    /**
     * Identifier field name for collection items
     *
     * Can be used by collections with items without defined
     *
     * @var string
     */
    protected $_idFieldName = 'post_id';

    /**
     * Initialization here
     *
     * @return void
     */
    protected function _construct(): void
    {
        $this->_init(Post::class, PostResource::class);
    }
}

Step 6: Define the Admin Grid UI Component and Layout

To display the grid in the Magento admin, you’ll need two things: a layout file to render the page and a UI component configuration to define the grid structure.

1. Admin Grid Layout

First, create a layout file to load the UI component for the grid. This layout file will define the URL for the grid and render the grid using the UI component.

Create the vendor_grid_index.xml file in the view/adminhtml/layout/ directory:

<?xml version="1.0" ?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
	<update handle="styles"/>
	<body>
		<referenceContainer name="content">
			<uiComponent name="vendor_grid_post_listing"/>
		</referenceContainer>
	</body>
</page>

This layout file tells Magento to use the UI component (vendor_grid_post_listing) and place it within the content block of the admin page.

2. UI Component Configuration

Now, define the UI component that will create the grid. This file includes the structure of the grid (columns, filters, sorting, etc.).

Create the vendor_grid_listing.xml file in the view/adminhtml/ui_component/ directory:

<?xml version="1.0" ?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="provider" xsi:type="string">vendor_grid_post_listing.vendor_grid_post_listing_data_source</item>
        </item>
    </argument>
    <settings>
        <spinner>vendor_grid_post_columns</spinner>
        <deps>
            <dep>vendor_grid_post_listing.vendor_grid_post_listing_data_source</dep>
        </deps>
    </settings>
    <dataSource name="vendor_grid_post_listing_data_source" component="Magento_Ui/js/grid/provider">
        <settings>
            <storageConfig>
                <param name="indexField" xsi:type="string">post_id</param>
            </storageConfig>
            <updateUrl path="mui/index/render"/>
        </settings>
        <aclResource>Vendor_Grid::Post</aclResource>
        <dataProvider name="vendor_grid_post_listing_data_source"
                      class="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider">
            <settings>
                <requestFieldName>id</requestFieldName>
                <primaryFieldName>post_id</primaryFieldName>
            </settings>
        </dataProvider>
    </dataSource>
    <listingToolbar name="listing_top">
        <settings>
            <sticky>true</sticky>
        </settings>
        <bookmark name="bookmarks"/>
        <columnsControls name="columns_controls"/>
        <filters name="listing_filters"/>
        <paging name="listing_paging"/>
    </listingToolbar>
    <columns name="vendor_grid_post_columns">
        <selectionsColumn name="ids">
            <settings>
                <indexField>post_id</indexField>
            </settings>
        </selectionsColumn>
        <column name="post_id">
            <settings>
                <filter>text</filter>
                <sorting>asc</sorting>
                <label translate="true">ID</label>
            </settings>
        </column>
        <column name="title">
            <settings>
                <filter>text</filter>
                <label translate="true">title</label>
            </settings>
        </column>
        <column name="content">
            <settings>
                <filter>text</filter>
                <label translate="true">content</label>
            </settings>
        </column>
        <column name="created_at">
            <settings>
                <filter>text</filter>
                <label translate="true">created_at</label>
            </settings>
        </column>
    </columns>
</listing>

This UI component file defines the columns (entity_id, title, created_at) that will be displayed in the grid.


Step 7: Menu, ACL Configuration, Dependency Injection (DI), and Route

In this step, we’ll set up the admin menu, access control (ACL), dependency injection (DI) configuration, and route to access the grid from the Magento admin panel.

1. Admin Menu Configuration

The admin menu configuration allows your grid to be accessible via the Magento admin sidebar.

Create the menu.xml file in the etc/adminhtml/ directory:

<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd">
    <menu>
        <add id="Vendor::top_level" title="Vendor" module="Vendor_Grid" sortOrder="9999"
             resource="Magento_Backend::content"/>
        <add id="Vendor_Grid::vendor_grid_post" title="Post" module="Vendor_Grid" sortOrder="9999"
             resource="Magento_Backend::content" parent="Vendor::top_level" action="vendor_grid/post/index"/>
    </menu>
</config>

This file defines a new menu item under the ID Vendor_Grid::grid, which will link to the action URL vendor_grid/grid/index that we’ll set up in the routing configuration.

2. ACL (Access Control List) Configuration

Access Control Lists ensure that only authorized users can view or manage the grid. You need to create an ACL configuration file.

Create the acl.xml file in the etc/ directory:

<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
    <acl>
        <resources>
            <resource id="Magento_Backend::admin">
                <resource id="Vendor_Grid::Post" title="Post" sortOrder="10" />
            </resource>
        </resources>
    </acl>
</config>

3. Dependency Injection (DI) Configuration

We need to set up the DI configuration to inject dependencies into the grid’s data source and collection.

Create the di.xml file in the etc/ directory:

<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <virtualType name="Vendor\Grid\Model\ResourceModel\Post\Grid\Collection"
                 type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult">
        <arguments>
            <argument name="mainTable" xsi:type="string">vendor_grid_post</argument>
            <argument name="resourceModel" xsi:type="string">Vendor\Grid\Model\ResourceModel\Post\Collection</argument>
        </arguments>
    </virtualType>

    <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
        <arguments>
            <argument name="collections" xsi:type="array">
                <item name="vendor_grid_post_listing_data_source" xsi:type="string">
                    Vendor\Grid\Model\ResourceModel\Post\Grid\Collection
                </item>
            </argument>
        </arguments>
    </type>
</config>

This DI configuration injects the GridCollection resource model as the data source for the grid and configures the vendor_grid table for the collection.

4. Routing Configuration

Finally, create a route for your admin grid to make it accessible via the URL.

Create the routes.xml file in the etc/adminhtml/ directory:

<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="admin">
        <route frontName="vendor_grid" id="vendor_grid">
            <module before="Magento_Backend" name="Vendor_Grid"/>
        </route>
    </router>
</config>

This routing file defines the vendor_grid route, which will be accessible through the vendor_grid/grid/index action URL. It maps the route vendor_grid to the Vendor_Grid module.


Step 8: Create the Controller

Create the Index controller to handle the grid display.

Controller File:

<?php

declare(strict_types=1);

namespace Vendor\Grid\Controller\Adminhtml\Post;

use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\ResultInterface;
use Magento\Framework\View\Result\PageFactory;

class Index extends Action
{
    /**
     * @param Context $context
     * @param PageFactory $resultPageFactory
     */
    public function __construct(
        Context $context,
        private readonly PageFactory $resultPageFactory
    ) {
        parent::__construct($context);
    }

    /**
     * Index action
     *
     * @return ResultInterface
     */
    public function execute(): ResultInterface
    {
        $resultPage = $this->resultPageFactory->create();
        $resultPage->setActiveMenu('Vendor_Grid::Post');
        $resultPage->getConfig()->getTitle()->prepend(__("Post"));
        return $resultPage;
    }
}


Step 9: Run Setup Upgrade

Run the following command to update the database and register the module:

php bin/magento setup:upgrade

Step 10: Testing the Admin Grid

Once the Admin Grid is fully set up, it’s crucial to test it in the Magento admin panel to ensure that everything works as expected.

1. Access the Admin Panel

  1. Log in to the Magento admin panel.
  2. Navigate to the menu section where you added your custom grid. For example, you should see Custom Grid in the menu based on our previous menu.xml configuration.
  3. Click on the Custom Grid menu item.

2. Verify the Grid Display

To verify your Grid Display:

  1. Open your browser.
  2. Go to the following URL:
http://your-magento-site/admin/vendor_post/post/index/
  • your-magento-site – needs to be changed to your domain.
  • admin – needs to be changed to your admin page path.

If everything is configured correctly, you should see the grid displayed on the page.


Conclusion

You’ve now created a basic Admin Grid in Magento 2! This grid can be extended to handle custom filters, mass actions, and much more. Admin Grids are powerful tools for managing data in Magento’s backend, and mastering them is essential for advanced Magento development.