[{"data":1,"prerenderedAt":35},["ShallowReactive",2],{"DNSdBEIOet":3},{"id":4,"slug":5,"title":6,"excerpt":7,"meta_title":6,"meta_description":8,"page_content":9,"featured_image":25,"fields":26,"tags":28,"published":31,"published_at":32,"created_at":33,"updated_at":34},"c0420bef-70f7-45a1-a041-3e820eff7d3b","notes/magento-2-module-development-for-beginners","Magento 2 Module Development for Beginners","Rewritten from my 2021-2022 videos: the three files every Magento 2 module needs, the commands that register it, blocks and dependency injection, routes and collections, and when to build a module instead of editing your theme.","The three files every Magento 2 module needs, the commands that register it, blocks and dependency injection, routes, and when to build a module not a theme.",{"blocks":10},[11,19],{"id":12,"data":13,"type":18},"6c8a8fb1-aa29-44f8-acf5-1abada1c3817",{"url":14,"title":15,"provider":16,"subtitle":17},"https://www.youtube.com/watch?v=VfR1CaL62rA","Magento 2 - module development for beginners (2021)","youtube","The original video this piece is based on.","video",{"id":20,"data":21,"type":24},"67b0a8df-c671-47b3-8e35-14eacfb4a845",{"content":22,"maxWidth":23},"\u003Cp>This is rewritten from a run of videos I recorded between 2021 and 2022, pulled together because the questions behind them have not gone away. People still email me asking how to build their first Magento 2 module, and the fundamentals have held up even though the version numbers have moved on. I was working on Magento 2.4 in the originals, so treat the specific paths as the shape of the thing rather than a line to copy blind. The thinking is what carries over.\u003C/p>\u003Ch2>The three files every module needs\u003C/h2>\u003Cp>A module is less mysterious than it looks. All modules live under \u003Cstrong>app/code/Vendor/ModuleName\u003C/strong>, and to be recognised at all a module needs exactly three files. There is \u003Cstrong>registration.php\u003C/strong>, which tells Magento the module exists and where to find it. There is \u003Cstrong>etc/module.xml\u003C/strong>, which declares the module name and its version. And there is \u003Cstrong>composer.json\u003C/strong>, which has quietly become a requirement rather than a nicety. Get those three in place, run the setup command, and Magento sees your module. Everything else you add is just behaviour layered on top of that skeleton.\u003C/p>\u003Cp>To pull it into the system you run \u003Cstrong>bin/magento setup:upgrade\u003C/strong>. That picks up any newly added module, installs it, and runs any schema or data it carries. One honest warning from the video that still catches people out: setup:upgrade re-enables caches that you had switched off. While you are developing locally I would turn the layout, block_html and full_page caches back off straight after, otherwise you will swear your changes are not working when really you are looking at a cached page.\u003C/p>\u003Ch2>Do not hand-write the boilerplate\u003C/h2>\u003Cp>In the original I leaned on a VS Code extension called Magento Wizard to scaffold the files. Tooling like that comes and goes and I would not promise any particular extension is well maintained today, so check before you install. The principle stands regardless: if you are writing registration.php and module.xml by hand every time, you are wasting your life. Use a generator, or keep a clean reference module you copy from. The boilerplate is identical on every project. Spend your attention on the part that is actually yours.\u003C/p>\u003Ch2>Blocks and dependency injection\u003C/h2>\u003Cp>Once the shell exists, the block is usually where a beginner does their first real work. A block sits in the back end but it is the closest thing to front end output, because it does the processing and then hands values to a template. The fastest way to understand it is to break it on purpose. Point your layout XML at your block class, call a method that does not exist yet, and read the error. Magento in developer mode will tell you almost exactly what it wanted. That is the loop: read the error, work out which class or method it expects, add it, move on.\u003C/p>\u003Cp>Dependency injection is the part that intimidates people and it should not. You do not new up Magento's classes, you ask for them in your constructor and Magento hands them over. If your block needs the product, or a CMS block factory, or a config value, you declare it as a constructor argument and use it. Most core blocks follow the same pattern, so when you are stuck, go and read the core block closest to what you want and copy how it injects its dependencies. The patterns repeat. Once you have seen a handful, new ones stop being scary.\u003C/p>\u003Ch2>Setup scripts, and what replaced them\u003C/h2>\u003Cp>In the older videos I showed an upgrade data script using setup_version and the UpgradeData interface to push a CMS block into the database when the module installed. That worked and you will still see it in the wild. Be aware, though, that Magento moved on from it: declarative schema handles database structure now, and data patches replaced the old data scripts from 2.3 onward. So learn the idea, which is that a module can seed its own data and version that work, but if you are writing this fresh today, reach for a data patch class rather than the version-numbered UpgradeData approach. Same goal, cleaner mechanism.\u003C/p>\u003Ch2>Routes and collections\u003C/h2>\u003Cp>The next step up is giving your module its own page. You register a route with a frontName in routes.xml, add a controller for the action, and Magento will answer requests on that URL. A neat first project is a custom listing, say a new-in products page, where you load a collection, filter it, and render it with the same toolbar and layout the category page uses. The lesson I kept hammering in the original is not the code, it is the method. Magento problems are all slightly different, and the skill that matters is reading the error, forming a guess, and testing it. Every problem you fight through makes the next one faster.\u003C/p>\u003Ch2>Module or theme? The rule I use\u003C/h2>\u003Cp>The question that comes up constantly is whether a change belongs in a module or just in the theme. My rule has not changed in years. Modules are structural and functional. Themes are design and brand. Adding a logout button to the account navigation through layout XML can technically go straight in your theme, quick and dirty, and sometimes that is the right call. But functionality that the store needs to work, rather than the way it looks, belongs in a module, because the next request is rarely the last. Today it is a logout link, next month it is the orders grid and the address book, and you will be glad it lived in a module you can reuse rather than buried in one brand's theme.\u003C/p>\u003Ch2>Where this sits now\u003C/h2>\u003Cp>All of this is Magento module development as I practised it for years, and if you maintain a Magento store it is still exactly how you extend one properly. I will be straight about the bigger picture though: the weight of this stack is part of why I moved a lot of my work to headless builds, which I wrote about in \u003Ca href=\"/notes/why-i-moved-from-magento-to-headless\">why I moved from Magento to headless\u003C/a>. Learn modules anyway. Plenty of live stores depend on them, the dependency-injection discipline makes you a better developer wherever you go next, and it pairs naturally with the theme side I covered in \u003Ca href=\"/notes/magento-theme-development-from-scratch\">Magento theme development from scratch\u003C/a>.\u003C/p>","lg","wysiwyg","",{"author":27},"Headless Digital",[29,30],"magento","dev-workflow",true,"2026-07-20T08:00:00+00:00","2026-06-22T07:20:00+00:00","2026-07-20T08:00:01.624754+00:00",1784534482335]