Getting started with a custom theme in Magento 2
The original video this piece is based on.
This is rebuilt from a run of videos I recorded back in 2021, when I was working through a full Magento 2 theme build on camera, version 2.4.1 heading into 2.4.2. I have rewritten it as one piece because the exact commands have aged but the way you should think about a custom theme has not. I have since moved most of my work to headless front ends, so treat this as how I did Magento themes properly when Magento was the right tool, not as a claim that you should still be starting new projects this way in 2026.
The single most useful habit I picked up doing this is that you never need to touch the admin to set a theme live. You can register it, assign it, and develop against it entirely from the codebase. Once that clicks, theme work stops feeling like fighting the platform.
Start with a child theme, not the core
The first rule is the boring one: do not edit Luma, and do not edit blank. Make your own theme that inherits from one of them. A child theme means a theme.xml with a parent set, a registration.php so Magento knows the theme exists, and a sensible package name like Vendor/mytheme. That is the whole setup. From there you only override the files you actually change, and every Magento update still flows through the parent.
I inherited from Luma rather than blank for client work, because Luma gives you a working checkout and account area out of the box and you really do not want to rebuild those from nothing. If you want full control and you are comfortable building structure yourself, blank is the cleaner starting point. That choice is the first real decision, and most people get it wrong by trying to gut Luma instead of extending it.
Assign the theme from config, not the admin
Here is the part that saves the most time. Instead of logging into the admin and picking your theme from a dropdown, set it in app/etc/config.php under the design config and import it. Magento will ask you to confirm the changed config, you run setup upgrade, and your theme is live. No clicking around, and the setting is in version control where it belongs.
The same applies to switching off the layout cache and full page cache while you work. I leave those off locally and run in developer mode so I actually see errors instead of a white page. The trade is that developer mode rebuilds static content on the fly when you refresh, which is slower, so I still force a static content deploy now and again to make sure nothing is quietly failing to compile.
Get your styling pipeline sorted before you write any CSS
Magento ships with LESS and the official front end build, and I never enjoyed it. I built around Gulp compiling SCSS straight to the theme's styles.css, which is the file Luma already expects, so nothing downstream needs to know how that file got there. For a base I used a small SCSS boilerplate called Primitive: a grid, a bit of typography, buttons, forms, and a handful of helpers. Enough to be useful, small enough to delete what you do not need.
I put that Gulp setup on GitHub as magento2-gulp-theme so I could drop it into any project and start. The point is not the tool. Webpack Mix or Laravel Mix would do the same job, and I moved to Mix on later builds. The point is that you decide how CSS gets compiled once, at the start, and then you forget about it and write styles.
Theme or module? Decide per change
This is the question that separates people who know Magento from people who are guessing. Some customisations belong in the theme: layout XML, templates, styles, moving or removing blocks. Some belong in a module: anything you want to be editable in the CMS, anything with logic, anything you would reuse across sites.
The header is the clearest example. Moving the existing Luma blocks around is pure layout XML, so that lives in the theme. But when I wanted an editable global header block the client could change without a developer, or a mega menu with its own behaviour, that became a small module. A rough rule I still trust:
- Presentation and positioning, where it sits and how it looks, goes in the theme.
- Content the client edits, or anything with logic, goes in a module.
- If you are copying the same override into a second site, it should probably have been a module.
Layout XML is the actual skill
You can get a surprising distance with a default.xml that removes the blocks you do not want and moves the ones you do. Most of "theming Magento" is really learning where the reference and move and remove instructions go, and resisting the urge to copy whole template files when a few lines of XML would do. Every template you copy is a file you now have to maintain through upgrades, so I copied as few as I could get away with.
Would I still do it this way?
For an existing Magento store, yes, almost exactly like this. Child theme, config-driven assignment, a build pipeline you control, and a clear line between theme and module. It is a sane setup and it survives upgrades.
For a new build, I would think hard before starting here at all. The reason I went down the headless route is that a lot of this effort goes into wrestling a heavy framework's front end, and a separate front end against the GraphQL API sidesteps most of it. I wrote about that decision in why I moved from Magento to headless. If you are on Magento and need the theme done properly, this is the way. If you are choosing a platform from scratch, that is a different conversation, and one I am happy to have.