How to Create a Magento 2 Child Theme Step by Step
Every Luma based Magento 2 store should run on its own theme, even when the design is close to Luma. Editing files inside vendor/magento/theme-frontend-luma is lost on the next Composer install, and changing core templates in place makes upgrades a guessing game. A child theme gives you a place for every customisation while the parent keeps receiving fixes.
This guide creates a child theme of Luma in Magento 2.4.8, step by step, and explains the parts that usually go wrong.
Luma or Blank as the parent
Both are valid parents. Luma is itself a child of Blank and adds its visual style on top. Pick Luma when the design is close to it and you want to change colours, fonts and some components. Pick Blank when the design is very different, so you do not spend time undoing Luma styles.
If you are starting a new store and performance matters, also look at Hyvä before committing to either. The Hyvä vs Luma comparison covers the trade offs. The rest of this guide assumes a Luma parent.
Step 1: the folder
Themes live in app/design/frontend/<Vendor>/<theme>. Use your company or client name as the vendor and a short lowercase name for the theme:
mkdir -p app/design/frontend/Acme/default/{etc,web/css/source,web/images,media,Magento_Theme/layout}
You can also ship a theme as a Composer package under vendor/, which is how theme vendors distribute them. For a store specific theme, app/design is simpler.
Step 2: theme.xml
<?xml version="1.0"?>
<theme xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Config/etc/theme.xsd">
<title>Acme Default</title>
<parent>Magento/luma</parent>
<media>
<preview_image>media/preview.jpg</preview_image>
</media>
</theme>
The parent value is the theme code, Vendor/name. The preview image is what the admin shows in the theme list. It is optional, but if you declare it the file must exist.
Step 3: registration.php
<?php
declare(strict_types=1);
use Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(
ComponentRegistrar::THEME,
'frontend/Acme/default',
__DIR__
);
The second argument is the area followed by the theme path. A typo here is the most common reason a theme does not appear in the admin.
Step 4: composer.json
For a theme in app/design, Composer does not load it, but a composer.json still documents dependencies and lets you move the theme to its own package later:
{
"name": "acme/theme-frontend-default",
"description": "Acme storefront theme",
"type": "magento2-theme",
"require": {
"magento/theme-frontend-luma": "*"
},
"autoload": {
"files": [
"registration.php"
]
}
}
Step 5: register and apply the theme
bin/magento setup:upgrade
bin/magento cache:flush
setup:upgrade registers new themes in the theme table. Then go to Content, Design, Configuration in the admin, edit the store view row, choose the new theme and save. You can apply it per website or per store view, which is useful when you want to test the new theme on one store view first.
Applying a theme writes to core_config_data under design/theme/theme_id. If you manage configuration in app/etc/config.php with app:config:dump, include the theme so it stays the same across environments.
Step 6: images and view.xml
A child theme inherits the parent's etc/view.xml, which defines product image sizes, gallery options and JS bundling settings. If you need to change any of those, copy the parent file from vendor/magento/theme-frontend-luma/etc/view.xml into Acme/default/etc/view.xml and edit it there. The child file is merged with the parent, but in practice teams copy the whole file to keep it readable.
After changing image sizes, resize the catalog images so the cache matches the new sizes:
bin/magento catalog:images:resize
On large catalogs this takes a long time. Run it during a quiet period or use the async option, --async, which queues the work for the media.storage.catalog.image.resize consumer.
Step 7: logo
The logo is set in layout XML. Place the file in web/images/logo.svg and point the logo block at it in Magento_Theme/layout/default.xml:
<?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">
<body>
<referenceBlock name="logo">
<arguments>
<argument name="logo_file" xsi:type="string">images/logo.svg</argument>
<argument name="logo_width" xsi:type="number">180</argument>
<argument name="logo_height" xsi:type="number">48</argument>
</arguments>
</referenceBlock>
</body>
</page>
Setting width and height helps the browser reserve space for the logo before it loads, which avoids a layout shift in the header. A logo uploaded in the admin under Content, Design, Configuration overrides this file.
How fallback works
When Magento looks for a template, layout file, image or stylesheet, it checks the current theme first, then each parent theme, then the module. That gives you three ways to customise the theme:
- Templates: copy a template from the module into the same relative path under the module folder in your theme.
vendor/magento/module-catalog/view/frontend/templates/product/view/details.phtmlbecomesAcme/default/Magento_Catalog/templates/product/view/details.phtml. - Layout: add a file with the same handle under
Magento_Catalog/layout/. It extends the module and parent files instead of replacing them. The layout XML guide explains the instructions. - Styles: add
web/css/source/_extend.lessand_theme.less. The LESS customization article explains which to use.
Copy as few templates as possible. Each copied template stops receiving fixes from the module, including security fixes in escaping. Before copying, check whether a layout argument, a view model or a plugin can do the job. When you do copy, keep a list and diff the copies against the new core version on each upgrade.
Translations in the theme
A theme can carry its own dictionaries in i18n/en_US.csv, i18n/pt_BR.csv and so on. Theme translations override module ones, which makes them a good place for wording changes that are specific to this storefront. The translations guide covers the details.
Deploying the theme
In developer mode, Magento generates static files on demand. In production mode, deploy them for your theme and every locale you use:
bin/magento setup:static-content:deploy en_US pt_BR --theme Acme/default --theme Magento/backend -f
The -f flag is only needed outside production mode. Deploying only the themes you use is faster than deploying all of them. Luma itself does not need to be deployed if no store view uses it directly, because its files are already included through fallback in your theme output.
Common problems
- The theme does not appear in the admin list. Check
registration.php, then runsetup:upgradeagain. - The store looks unstyled after switching. Static content was not deployed for the new theme, or a custom
_theme.lessreplaced the parent one without copying its variables. - Template changes do not show. Clear
var/view_preprocessedand the layout and block caches, and confirm the path matches the module path exactly.
FAQ
Can a child theme have its own child theme?
Yes. Fallback walks through every parent in the chain. Luma is already a child of Blank, so a theme based on Luma is a third level theme.
Do I need a module to create a theme?
No. A theme only needs theme.xml and registration.php. Use a module when you need PHP logic such as view models, plugins or controllers, and keep the theme for presentation.
Why is my theme missing after deploying to another server?
The theme table on that server was not updated. Run bin/magento setup:upgrade as part of the deploy, and keep the theme assignment in config.php if you manage configuration in code.
Can I use different themes per store view?
Yes. Content, Design, Configuration lets you assign a theme per website or per store view, so a B2B store view can run a different theme from the main store.
I am available for freelance Magento and Adobe Commerce work, from theme setup to full storefront builds. If you need help with this, get in touch.
Related articles
Luma to Hyvä Migration Plan: Audit, Phases and Launch
Luma to Hyvä migration plan for Magento 2: extension and theme audit, checkout decision, page by page rebuild, SEO parity, testing, launch and rollback.
Hyvä vs Luma in 2026: What Changes for a Magento Developer
Hyvä vs Luma from a developer's view: Alpine.js and Tailwind vs RequireJS, Knockout and LESS, extension compatibility, checkout options and when to pick each.
Magento 2 Layout XML: A Practical Guide for Developers
How Magento 2 layout XML works: handles, containers, blocks, move and remove, arguments, view models and page layouts, with examples that hold up in 2.4.8.
Magento 2 LESS Customization: _theme.less, _extend.less and More
How to customize Luma styles with LESS in Magento 2: variables in _theme.less, additions in _extend.less, module overrides, media queries and Grunt compiling.