How to Override a Template in Magento 2

One of the most common tasks for a Magento 2 developer is overriding a template. Magento 2 makes this easy in most cases, but there are some cases that will leave you wondering if it’s even possible. This article will cover which methods for overriding a template are appropriate for a variety of cases and when you should consider pursuing an alternative to a template override.

In Magento 2, there are two main methods for overriding a template:

  • Theme file path
  • Layout Block Argument

The template path method is used when building a theme and the layout method is used when building a module. They are the simplest, most straight forward, and easiest to understand methods. You will almost always be using one of these two methods. For the very few times these two methods are not available, you have two other methods to choose from:

  • Class preference
  • Plugin

If you would like to avoid confusion or information overload, it is acceptable to skip the other two options, then come back in the future when you run into a template that you cannot override with the two conventional methods.

Theme File Path

In Magento 2, themes can override any module’s or parent theme’s layout, template, or web (css, js, etc.) file simply by placing it in <theme_dir>/<Vendor>_<Module>/path/to/file. For example, If you want to override the template located at <theme_dir>/<Vendor>_<Module>/view/html/header.phtml for the Magento_Theme module, you would place your template in <theme_dir>/Magento_Theme/templates/html/header.phtml.

There are several block definitions in Magento 2 that do not have the Vendor_Module prefix to specify which module the template belongs to. In these cases, the block’s class attribute will define what module the template belongs to. For example if you were to find a block definition in the Magento_Checkout module like this <block class="Magento\Checkout\Block\SomeBlock" template="someTemplate.phtml" /> you would place your template inside the Magento_Checkout module directory inside your theme.

This Magento 2 devdoc on theme-inheritance goes into more detail on how to override a template in a theme.

Layout Block Argument

The layout method should be used when building a module. To override a template using layout XML, you only need to override the block’s template argument. Using the template Magento_Wishlist/view/frontend/templates/view.phtml as an example, to override view.phtml with your own template, you first have to create a new layout file. <Vendor>_<Module>/view/frontend/layout/wishlist_index_index.xml

There are currently two methods of overriding a block argument.

(New method)

wishlist_index_index.xml

(Old deprecated method)

wishlist_index_index.xml

The devdocs say that the new method is the appropriate method to override a template with layout XML but according to issue #3356 on the Magento 2 GitHub repo, there are a number of cases where the new method won’t work. In these cases it is okay to use the old deprecated method until the issue is resolved.

Now you have to place your new custom template in the location you specified in your layout file. For this example, that is <Vendor>_<Module>/view/frontend/templates/view.phtml

The path of the template in a module does not matter so long as it matches the path you set in your template attribute. I like to put the template in the same path you found it in its original module starting from the module’s templates directory but add a directory for the module name of the template you’re overriding. For example, in your module, you would put a wishlist template in <Vendor>_<Module>/view/frontend/templates/wishlist/view.phtml or for a catalog template it would be <Vendor>_<Module>/view/frontend/templats/catalog/view.phtml.

There is one additional step for your override to take effect. You must add a sequence to your module.xml file for the module containing the layout file you are modifying. For this example, your etc/module.xml file will look like this.

module.xml

This will ensure that the Magento_Wishlist module will be loaded and added to the merged layout file before your module. This is necessary as it ensures your module’s layout override will be parsed after the layout XML it is referencing has been parsed. Otherwise, your layout could be referencing something that doesn’t exist yet, so your layout override will not apply.

Class Preference

You may have found that there are some block definitions that contain a template attribute but do not contain a name attribute. This means you can’t override the block from layout XML like you normally would in a module. If the block’s template attribute does not contain the Vendor_Module prefix you can utilize the class preference method to override the template.

In this example, we will be overriding the block containing the cart/item/default.phtml template in found in Magento/Checkout/view/frontend/layout/checkout_cart_item_renderers.xml.

The class attribute on the block sets the scope for the template path so a Vendor_Module prefix is unnecessary if the template is in the same module as the block’s class. This means that all it takes to change the template’s module scope is to change the block’s class with a class preference in your di.xml file like this.

di.xml

Now you have to make sure that block actually exists even though you don’t actually need to do anything with it. To do this you can just create a skeleton block.

Renderer.php

To compleate the template override you just need to add your template like you would if you were doing doing a normal template override from layout. In this case, that path will be Vendor/Module/view/frontend/templates/cart/item/default.phtml

At this point, it should be obvious how big of a hack this is. It is only prudent in an extremely small number of cases. If you think you might have such a case, consider if there are any alternatives like the ones I will list later in this article. Consider the ramifications for other modules that may want to do something similar to what you are doing. Hopefully this issue will eventually be fixed and this sort of hack will no longer be necessary.

Plugin

The class preference is an acceptable option when you are wanting to override the template for all instances of a class and the Vendor_Namespace prefix is missing from the template attribute on block definition. However, there may be times when you need to be more targeted. The class may be used for multiple templates or the Vendor_Module prefix might be set. In these cases, a plugin is the best option. For this example we will override Magento_Catalog::category/products.phtml with our own template using a plugin. I won’t be going into great detail on plugins so if you aren’t already familiar with them, please read through the Magento 2 devdoc on plugins before continuing.

The original block definition for the category view template looks like this

So our plugin will need to hook into the class Magento\Catalog\Block\Category\View. The toHtml() method, inherited by Magento\Framework\View\Element\Template, retrieves the template and turns it into html. To override the template that gets retrieved by that method, the $_template variable needs to be changed to your own template. Fortunately, the Template class already has a method to do that. With this understanding, we can create our plugin and di.xml files.

di.xml

View.php

The plugin could be set on a few other methods but I chose toHtml because it is the method in which the template is first used. This method of overriding a template is not recommended except when necessary. It should only be used if the block does not have a name, has a Vendor_Module prefix and/or has a class that handles multiple templates. To complete the template override just place your template in your module in the appropriate location, <Vendor>/<Module>/view/frontend/templates/catalog/category/products.phtml

Which method should you use?

There is no one particular method that should be used in all scenarios. If you are building a theme, use the template path method. If you are building a module, use the layout method. For developers trying to figure out where a core Magento template has been overridden, these are the two places they will look. If you are building a module and you encounter a block that cannot be referenced by name, you can use one of the two unconventional methods to override that block’s template.

Alternatives

To modify something on a page, overriding a template is not always the best option. Another module could override your override in any of the four methods, the name on a block could change with an update, or the class assigned to a block could change. Of course in many situations it is the best, if not the only option. However, there are some occasions when it would be better to go another route.

The layout structure allows for inserting child blocks

Many times, the only change required to a template is an addition to the beginning or end of that template. In these cases, if the block defining the template you want to override is nested inside a container, you can simply create your own block and place it before or after the block you previously wanted to override. One of the distinguishing characteristics between blocks and containers is that children of blocks must be explicitly called in order to be rendered while children of containers will render all of their children blocks and containers. So all that is required to add your new markup to the wishlist page is to create your own layout/wishlist_index_index.xml file, like this:

You only need to remove an element

This isn’t recommended but for cases where you really want to avoid overriding a template to remove an element, you can remove the element with JS or CSS. It’s easy to think this is a good idea in terms of ease of implementation but it usually is not the best solution. If used too frequently, you can have large chunks of html that get loaded but aren’t displayed to the user. This can increase page load times and lead to messy, hard to maintain HTML and CSS. There is really no clear line for what is “too frequent” so it is best to avoid this option unless absolutely necessary.

You need to replace a jQuery widget with your own

Overriding JS in Magento 2 doesn’t not always require overriding the template where it gets initialized. You can override functions and objects on a jQuery UI widget much like you would override a variable or method on a PHP class you are extending. To do this, first create your JS file in your theme or module:

  • Theme: <Vendor>/<Theme>/web/js/customAccordion.js
  • Module: <Vendor>/<Module>/view/frontend/web/js/customAccordion.js

customAccordion.js

Now create a mapping for your customAccordion widget:

  • Theme: <Vendor>/<Theme>/requirejs-config.js
  • Module: <Vendor>/<Module>/view/frontend/requirejs-config.js

requirejs-config.js

Now, anywhere accordion is included or initialized, your custom accordion will be loaded. For more information. Check out this Magento 2 devdoc: Customize a default magento jQuery widget

You want to change a line of text

Magento 2 has a very easy method of overriding strings of text. You can do this by adding a translation to your your theme or module that matches the line of text you want to change and replaces it with any string you set. This approach isn’t considered a best practice, but sometimes it is worth the tradeoff as opposed to having to override a large template to make a simple text change. To learn more about this, read this guide on the Magento 2 devdocs: Use translation dictionary to customize strings. You need to be careful when using this method in case a string you’re translating is used in multiple locations.

Wrapping up

You’ve learned the good, the bad, and the ugly of Magento 2 template overrides. The two main methods of overriding a template are “good”, but not always necessary. The two “bad” methods of overriding a template are almost never necessary and even then should be considered a hack that needs to be replaced when it becomes possible. Template overrides are a quick and easy way to make changes in Magento 2 they can be easily abused. When you have the option, consider the alternatives to overriding a template.

Recent Posts

Leave a Comment