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="MagentoCheckoutModelCompositeConfigProvider">
       <arguments>
           <argument name="configProviders" xsi_type="array">
               <item name="cms_block_config_provider" xsi_type="object">VendorModuleNameModelConfigProvider</item>
           </argument>
       </arguments>
   </type>
   <type name="VendorModuleNameModelConfigProvider">
       <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 VendorModuleNameModelConfigProvider.

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

<?php namespace VendorModuleNameModel;
 
use MagentoCheckoutModelConfigProviderInterface;
use MagentoFrameworkViewLayoutInterface;
 
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('MagentoCmsBlockBlock')
           ->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 VendorModuleNameModel;
 
 
use MagentoCheckoutModelConfigProviderInterface;
use MagentoCmsBlockWidgetBlock;
 
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! 🙂