How to display CMS block in Magento 2 Checkout

How to display CMS block in Magento 2 Checkout

Working with CMS blocks was one of the reasons Magento was and is so popular. Using CMS blocks, site administrator can easily manipulate content of the store. CMS blocks can be used to display promotional banners, sale blocks, return policies, important information message on some sections of the store etc. CMS blocks can carry plain text or chunks of HTML/JS/CSS code which means they can be used for even more complex content delivery like sliders, product carousels etc.

I personally like to use CMS blocks whenever I can so that content of the store becomes more “modular” and easily manageable.

CMS blocks at checkout can have many usages like displaying some information for specific shipping method of some promotional content in order to convince the user to spend more before he checks out.

Adding CMS block to some specific position/page is not such a hassle, we just need to “register” our CMS block in layouts and define order/position of our CMS block. We then need to call it in templates and that’s it. CMS block is now part of the content and will be rendered/delivered when needed.

Adding CMS block to Magento 2 Checkout is a bit more complex task since the whole checkout is built up from a series of KnockoutJS components which are then rendered using the knockout.js templating system. Magento 2 defines these components and their mutual relationship in a large XML file. To pull the data from the server, Magento uses global Javascript variable window.checkoutConfig which is then used by Knockout.js to compute and display information on the frontend.

In short, the whole checkout is dynamically loaded and we can’t put anything “static” inside.

Hopefully, there is a way to insert CMS block inside checkout flow.

Since the whole checkout is bundled from various JS components, approaches on this task will vary depending on the CMS block position. However, each approach has the same initial step.

Idea is to take content from CMS block and put it in JS object which will then be outputted to frontend via Knockout Bindings. That way, our CMS content becomes part of the checkout flow and it is loaded alongside with other components. If you want to hang out with components, act as a component!

Creating a module

If you don’t know how to create Magento 2 module, follow steps in this article by my colleague Hrvoje Ivancic.

I would suggest that you use netz98 magerun CLI tools extension which is a helpful asset during development. For example, you can create a module with all necessary files with just one command.

Name the module as you wish. I will use Vendor for vendor and ModuleName for module name.

After you have created module with all necessary files, we will add/edit few of them to achieve our goals.
Please, don’t forget to do setup:upgrade after you have created your module.

Adding CMS block in Checkout sidebar

Edit Vendor/ModuleName/etc/frontend/di.xml file:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
   <type name="Magento\Checkout\Model\CompositeConfigProvider">
       <arguments>
           <argument name="configProviders" xsi:type="array">
               <item name="cms_block_config_provider" xsi:type="object">Vendor\ModuleName\Model\ConfigProvider</item>
           </argument>
       </arguments>
   </type>
   <type name="Vendor\ModuleName\Model\ConfigProvider">
       <arguments>
           <argument name="blockId" xsi:type="string">checkout_cms_block</argument>
       </arguments>
   </type>
</config>

With this code, we are adding new entry to ConfigProvider and we are also declaring CMS block which will be used to parse data from.
In the above example, a block is named “checkout_cms_block” (it works with id or identifier of the block).

Please make sure that and paths match the name of your block. In above example, path is Vendor\ModuleName\Model\ConfigProvider.

Next, we will create new entry for Config Provider by creating or editing Vendor/ModuleName/Model/ConfigProvider.php:

<?php namespace Vendor\ModuleName\Model;
 
use Magento\Checkout\Model\ConfigProviderInterface;
use Magento\Framework\View\LayoutInterface;
 
class ConfigProvider implements ConfigProviderInterface
{
   /** @var LayoutInterface  */
   protected $_layout;
   protected $cmsBlock;
 
   public function __construct(LayoutInterface $layout, $blockId)
   {
       $this->_layout = $layout;
       $this->cmsBlock = $this->constructBlock($blockId);
   }
 
   public function constructBlock($blockId){
       $block = $this->_layout->createBlock('Magento\Cms\Block\Block')
           ->setBlockId($blockId)->toHtml();
       return $block;
   }
 
   public function getConfig()
   {
       return [
           'cms_block' => $this->cmsBlock
       ];
   }
}

Since we have added our own variable to global window.checkoutConfig variable, we can now call that content using window.checkoutConfig.cms_block variable.

Now we just need to bind that variable to some HTML element and Knockout.JS will do the rest.

Here is app/design/frontend/Vendor/ThemeName/Magento_Checkout/web/template/sidebar.html

 <div id="opc-sidebar"
    data-bind="afterRender:setModalElement, mageInit: {
   'Magento_Ui/js/modal/modal':{
       'type': 'custom',
       'modalClass': 'opc-sidebar opc-summary-wrapper',
       'wrapperClass': 'checkout-container',
       'parentModalClass': '_has-modal-custom',
       'responsive': true,
       'responsiveClass': 'custom-slide',
       'overlayClass': 'modal-custom-overlay',
       'buttons': []
   }}">
 
   <!-- ko foreach: getRegion('summary') -->
       <!-- ko template: getTemplate() --><!-- /ko -->
   <!--/ko-->
 
   <div data-bind="html: window.checkoutConfig.cms_block"></div>
 
   <div class="opc-block-shipping-information">
       <!-- ko foreach: getRegion('shipping-information') -->
       <!-- ko template: getTemplate() --><!-- /ko -->
       <!--/ko-->
   </div>
</div>

And that’s it.

If you did everything right, you should see your CMS block displayed in the sidebar.

Please, note that I’ve created sidebar.html template in my package/theme. Never edit files directly in vendor/magento/* folder.

Adding CMS block to address form

For this task, we will use a bit different approach.
We will change the content of app/code/Vendor/ModuleName/Model/ConfigProvider.php
to this:

<?php
 
namespace Vendor\ModuleName\Model;
 
 
use Magento\Checkout\Model\ConfigProviderInterface;
use Magento\Cms\Block\Widget\Block;
 
class ConfigProvider implements ConfigProviderInterface
{
   protected $cmsBlockWidget;
 
   public function __construct(Block $block, $blockId)
   {
       $this->cmsBlockWidget = $block;
       $block->setData('block_id', $blockId);
       $block->setTemplate('Magento_Cms::widget/static_block/default.phtml');
   }
 
   public function getConfig()
   {
       return [
           'cms_block' => $this->cmsBlockWidget->toHtml()
       ];
   }
}

Then, create app/code/Vendor/ModuleName/view/frontend/layout/checkout_index_index.xml
with this content:

<?xml version="1.0"?>
 
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="checkout" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
   <body>
       <referenceBlock name="checkout.root">
           <arguments>
               <argument name="jsLayout" xsi:type="array">
                   <item name="components" xsi:type="array">
                       <item name="checkout" xsi:type="array">
                           <item name="children" xsi:type="array">
                               <item name="steps" xsi:type="array">
                                   <item name="children" xsi:type="array">
                                       <item name="shipping-step" xsi:type="array">
                                           <item name="children" xsi:type="array">
                                               <item name="shippingAddress" xsi:type="array">
                                                   <item name="children" xsi:type="array">
                                                       <item name="shipping-address-fieldset" xsi:type="array">
                                                           <item name="children" xsi:type="array">
                                                               <item name="cms-block" xsi:type="array">
                                                                   <item name="component" xsi:type="string">uiComponent</item>
                                                                   <item name="config" xsi:type="array">
                                                                       <item name="template" xsi:type="string">Vendor_ModuleName/cms-block</item>
                                                                   </item>
                                                                   <item name="sortOrder" xsi:type="string">1</item>
                                                               </item>
                                                           </item>
                                                       </item>
                                                   </item>
                                               </item>
                                           </item>
                                       </item>
                                   </item>
                               </item>
                           </item>
                       </item>
                   </item>
               </argument>
           </arguments>
       </referenceBlock>
   </body>
</page>

We have now added new UIcomponent to the address form. That component will hold the content of our CMS block. As I mentioned above, components load order is managed inside XML files. As you can see, I have set 1 for sortOrder with means my component will be loaded first inside address form.
Watch for this line Vendor_ModuleName/cms-block and change it.
Now that we have everything set, we just need a knockout template to output the CMS block data.

Create app/code/Vendor/ModuleName/view/frontend/web/template/cms-block.html
with this content:

<div data-bind="html: window.checkoutConfig.cms_block"></div>

And that’s it. Now your CMS block should be displayed in address form and rendered first.
Given examples work with clean Magento 2.2 installation. Luma theme was used for testing purposes.

If something is not working, please check:

  • is your module properly created and consist all config files with proper values inside?
  • did you run php bin/magento setup:upgrade after creating module?
  • check if you have properly changed code from this article by replacing Vendor/ModuleName with proper vendor and name of your module!

Thanks for reading! 🙂

Related Inchoo Services

You made it all the way down here so you must have enjoyed this post! You may also like:

Add static content in Magento Checkout address form Mladen Ristic
, | 2

Add static content in Magento Checkout address form

Don’t Mess with the Magento 2 Checkout Matej Djakovic
Matej Djakovic, | 31

Don’t Mess with the Magento 2 Checkout

Meet Magento Belarus features an Inchooer talking Magento 2 Checkout Ivona Namjesnik
Ivona Namjesnik, | 0

Meet Magento Belarus features an Inchooer talking Magento 2 Checkout

7 comments

  1. Hi Nenad, could you please help me solve this issue: https://magento.stackexchange.com/q/301963/2358

    I want to show HTML content in notice section for country_id field, do you know how we can do this using a customer module? I have cerated a module but not able to add HTML content.

    I have added proper details in the question link I have given, could you please help.

  2. I wish to add several information blocks in the checkout and trying to see how to adapt to have multiple

    The first method seems to hard code the name of the block in the di.xml definition and it seems like to put multiple blocks in multiple places we would have to duplicate ConfigProvider with different names.

    I am not sure looking at the second method where the name of the block is set – perhaps it also uses the same di.xml but this is not mentioned and not repeated?

    Is there a way I can use the same config provider and pass it a different block ID?

  3. Thanks for the good info, I need to add a custom block and PHTML file into the M2 checkout area. Is the method similar?

  4. Cool, thanks for the tutorial. It worked fine for me, but do I really have to create a separate module for each static block I want to add?

  5. Nice tutorial for adding CMS blocks into checkout step. It looks like very complicated. but the concept is simple and easy to follow. Nice Job.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.

Tell us about your project

Drop us a line. We'd love to know more about your project.