Thu Aug 27 2020 • ☕️☕️☕️ 17 min read • you like my writing? Buy me a coffee
Sitecore SXA is a great way forward in structuring the way of building Sitecore web sites, providing a set of standard renderings and provide an editing experience for the web site maintainers. But sooner than later you want to extend/replace the out-of-the-box set of renderings, calling in the front-end developers to do their magic. At that moment there are many ways they can do their work, and in this post, I will describe in what is in my opinion the optimal way for performance, SEO, front-end developer experience, and separation of concerns.
A good Sitecore SXA project needs good front-end developers, and good front-end developers don’t write ASP.NET Razor views and jQuery. We already had this problem a few years ago when we were done by the workflow of front-end developers writing a site mockup in HTML with SASS, and jQuery for the interactive elements, and back-end developers translating this into ASP.NET Razor views. That has not even the worst part - the trouble of keeping the changes to the often extensive amount of HTML in the mockup site in sync with the Razor views was the really bad part.
Because ReactJS + TypeScript was our defacto standard at that time we decided to write ReactsJS components, have a generic view controller render these React components on the server-side using ReactJS.NET, and hydrate on the client-side for interaction. As shown on the ReactJS.NET website you will get ASP.NET CSHTML Razor pages with code like:
<!-- This will render the component server-side -->
@Html.React("CommentsBox", new {
initialComments = Model.Comments
})
<!-- Initialise the component in JavaScript too -->
<script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.development.js"></script>
<script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.development.js"></script>
@Scripts.Render("~/bundles/main")
@Html.ReactInitJavaScript()
This approach works quite well, and my colleague Sander Boekenoogen even solved the puzzle to support dynamic placeholders within the React rendering. But there are also some down-sides to this approach:
To quote Michael Bublé:
“It’s a new dawn. It’s a new day. It’s a new life. For me…”
The big selling point of Sitecore SXA is its extensive set of out-of-the-box components, which gets even bigger when implementing Sitecore Commerce. These components are not the most modern and flexible components, written using jQuery and KnockoutJS (used in the search components and the SXA storefront). Front-end developers need to be able to write additional components in a modern way, but they should be able to work together with components written “in the old days”, so using jQuery and KnockoutJS. Meet web components. What are web components? As described on webcomponents.org:
Web components are a set of web platform APIs that allow you to create new custom, reusable, encapsulated HTML tags to use in web pages and web apps. Custom components and widgets build on the Web Component standards, will work across modern browsers, and can be used with any JavaScript library or framework that works with HTML.
Although my personal opinion is that we should support only modern browsers, so no more Internet Explorer 11 (it was released in 2013!), we have many customers that still want to be able to support Internet Explorer 11. Older versions of Internet Explorer are in my opinion a no-go, except if it is a very deliberate choice, and the customer is willing to pay for the extra effort (and the mental damage). A good example was a car tire company that still got many orders from Windows XP machines they installed in car garages. On these machines, all orders were done through Internet Explorer 8.
Back to Internet Explorer 11… It has no support for web components, but there is a good polyfill to make the custom elements part of web components work on Internet Explorer 11. I think this is good enough. Optimized for modern browsers, but still working on Internet Explorer 11.
There are multiple ways to write web components, but in the end, a web component is an addition or extension of the set of available HTML elements and can be used just like a normal HTML element:
<popup-info
img="img/alt.png"
data-text="Your card validation code (CVC)
is an extra security feature — it is the last 3 or 4 numbers on the
back of your card.">
</popup-info>
and the good thing is: web components are part of existing web standards, it is not just another framework that comes and goes.
Web components are based on four main specifications as described here, and quoted below:
Custom Elements - The Custom Elements specification lays the foundation for designing and using new types of DOM elements
Shadow DOM - The Shadow DOM specification defines how to use encapsulated style and markup in web components
ES Modules - The ES Modules specification defines the inclusion and reuse of JS documents in a standards-based, modular, performant way
HTML Template - The HTML template element specification defines how to declare fragments of markup that go unused at page load, but can be instantiated later on at runtime
There are multiple ways how you can write web components. In my investigations, I moved from the plain way as described in the Mozilla documentation, to using LitElement by the Google Polymer team and advocated by the https://open-wc.org/ team, ending up by StencilJS by the Ionic team that, in my opinion, provides the most powerful vision and tooling. Another important reason why I chose StencilJS was that I wanted to build web components without using shadow DOM because that is difficult to support on Internet Explorer 11, but still use slots (placeholder inside a web component that you can fill with your own markup) because it allows supports for SEO. This is not supported by LitElement, although I got a dirty work-around up and running, while StencilJS supports this without a problem.
Using StencilJS I created two versions of an Address Card, one using attributes, and one using slots. Based on these versions I have done some research on how search engines see these different approaches. The address cards looks like:
The address card component has the following capabilities:
When the “show location” button is pressed, the card flips to show the map:
I wrote two versions of the address card, one with all data in attributes as you would do in a ReactJS component, and one where data can be injected through slots.
Using the address card web component with attributes looks like:
<dmp-addresscard
imgsrc="images/Amy Edwards.jpg"
name="Amy Edwards"
address="1212 Omaha Dr, Montgomery, AL 36105, United States"
lat="32.1479602" lon="-85.0162703"
email="amy.edwards@example.com" phonenumber="+1 334-280-0960"
phonenumberislink="true"
addressislink="true">
<hr class="mt-4" />
<strong>"The greatest glory in living lies not in never falling,
but in rising every time we fall."</strong>
<em>-Nelson Mandela</em>
<hr/>
</dmp-addresscard>
Using the address web component with slots looks like:
<dmp-slottedaddresscard
phonenumberislink="true"
addressislink="true"
lat="32.1479602" lon="-85.0162703">
<div slot="image"><img src="images/Amy Edwards.jpg" /></div>
<div slot="name">Amy Edwards</div>
<div slot="address">1212 Omaha Dr, Montgomery, AL 36105, United States</div>
<div slot="phonenumber">+1 334-280-0960</div>
<div slot="email">amy.edwards@example.com</div>
<div slot="block">
<hr class="mt-4" />
<strong>"The greatest glory in living lies not in never falling,
but in rising every time we fall."</strong>
<em>-Nelson Mandela</em>
<hr />
</div>
</dmp-slottedaddresscard>
I added those components to a set of pages that can be found at https://mango-river-020e4d803.azurestaticapps.net/ so they can be indexed by search engines. The most important pages are:
When I search for Amy Edwards site:mango-river-020e4d803.azurestaticapps.net
I get:
In the above results the first hit is the main page just mentioning Amy Edwards, the second hit is the index-slotted.html
page. The index-attributed.html
page where the name Amy Edwards is specified in attributes does not come back in the search result. Same happens when we search for Vanessa Adams which is specified in attributes, the index-attributed.html
page in NOT returned in the search results.
A similar SEO check was done in the Medium post Web Components and SEO, with the same conclusion that it is mot possible to use attributes for SEO relevant content.
In the documentation on Google Search in the section Follow best practices for web components it is stated that:
Googlebot supports web components. When Googlebot renders a page, it flattens the shadow DOM and light DOM content. This means Googlebot can only see content that’s visible in the rendered HTML. To make sure that Googlebot can still see your content after it’s rendered, use the Mobile-Friendly Test or the URL Inspection Tool and look at the rendered HTML.
If the content isn’t visible in the rendered HTML, Googlebot won’t be able to index it.
In my weblog post Creating Sitecore SXA 9.3+ renderings without C# I described how to create an Address Card rendering in Sitecore SXA using a datasource and parameters. I used a Scriban template for the Plain
rendering variant which writes out the content without any styling or interactivity.
The next step is to use our dmp-slottedaddresscard
web component within the Scriban template. The code for this web component can be found in the GitHub repository https://github.com/svdoever/sxa-umbrella-webcomponents. Building the web components in this repository results in a set of front-end artifacts that looks like:
There are some interesting observations to make about the above artifacts:
sxa-umbrella-webcomponents.css
for the styles, sxa-umbrella-webcomponents.esm.js
as the JavaScript bundle for modern browsers, sxa-umbrella-webcomponents.js
as the JavaScript bundle for Internet Explorer 11.sxa-umbrella-webcomponents.js
for Internet Explorer 11 is large (120 KB), this is because this bundle contains the polyfill to support web components on Internet Explorer 11.sxa-umbrella-webcomponents.esm.js
for modern browsers is really small (520 bytes) and knows how to dynamically load additional bundles (all the small JavaScript artifact files) when web components are used on the web page.It would be nice to make these artifacts part of a Sitecore SXA theme, but the problem is that we can’t change the filenames (auto-generated), and we can’t upload these files into the Sitecore Media Library as described in my LinkedIn post Why Sitecore sucks for hosting your front-end artifacts.
We could create an additional folder assets/sxa-umbrella-webcomponents
in the Sitecore web site to host these files which works perfectly well, but this means that we need to deploy these file to every CM and CD which is suboptimal for scaling, and it will not work in a future SAAS version of Sitecore.
Another option would be to publish the files on any website or CDN, and reference the files from there. I chose a different option for the production deploy, but with the same result: I published the artifacts as an NPM package with a version, and reference them through https://unpkg.com/. This allows referencing every version of the published artifacts which are served through a Cloudflare CDN.
During development We could create an IIS virtual directory at assets/sxa-umbrella-webcomponents
pointing to the dist/sxa-umbrella-webcomponents
folder so the sxa-umbrella-webcomponents build results in watch mode would become available immediately within Sitecore.
To reference the required files I added a HTML Snippet to the Metadata partial design from the Sitecore Experience Accelerator:
For copy/paste purposes the relevant lines of the HTML snippet:
<link rel="stylesheet" href="https://unpkg.com/sxa-umbrella-webcomponents@0.0.2/dist/sxa-umbrella-webcomponents/sxa-umbrella-webcomponents.css" />
<script type="module" src="https://unpkg.com/sxa-umbrella-webcomponents@0.0.2/dist/sxa-umbrella-webcomponents/sxa-umbrella-webcomponents.esm.js">
</script>
<script nomodule src="https://unpkg.com/sxa-umbrella-webcomponents@0.0.2/dist/sxa-umbrella-webcomponents/sxa-umbrella-webcomponents.js">
</script>
I’m still looking for a solution to make the base path configurable between development and production as described in this question on StackOverflow.
We load the JavaScript modules with differential module loading using the module-nomodule trick to load the correct bundle.
We can now use the dmp-slottedaddresscard
web component within the Scriban template, so I created an additional rendering variant for the Address Card rendering called WC Slots
with a Scriban item and the following Scriban template:
{{
if !o_pagemgde.is_experience_editor_editing && (sc_parameter 'PhonenumberIsLink')
phonenumberIsLink = "true"
else
phonenumberIsLink = "false"
end
}}
{{
if !o_pagemode.is_experience_editor_editing && (sc_parameter 'AddressIsLink')
addressIsLink = "true"
else
addressIsLink = "false"
end
}}
<dmp-slottedaddresscard
phonenumberislink={{phonenumberIsLink}}
addressislink={{addressIsLink}}
lat="{{i_item.Latitude.raw}}" lon="{{i_item.Longitude.raw}}">
<div slot="image">{{i_item.Image}}</div>
<div slot="name">{{i_item.Name}}</div>
<div slot="address">{{i_item.Address}}</div>
<div slot="phonenumber">{{i_item.Phonenumber}}</div>
<div slot="email">{{i_item.Email}}</div>
<div slot="block">
{{sc_placeholder "dynAcSlot"}}
</div>
</dmp-slottedaddresscard>
Selecting the WC Slots
rendering variant for the Address Card rendering results in:
The WC Slots
Address Card rendering variant contains a dynamic placeholder where I can place a Page Content / Rich Text
rendering to render the rich text of quotes as an example:
But it is possible to place any standard SXA rendering in the dynamic placeholder. For example a Composites / Carousel
like in the image below:
In the Experience Editor it is also possible to edit the texts on the Address Card directly:
Because web components provide additional custom HTML elements that can be used just like any standard HTML element, these elements can be rendered server-side through Scriban, Razor pages (CSHTML) or PHP if you like. They can also be used from any front-end framework like ReactJS, Vue or Angular. This makes them the ultimate building blocks for your design system of components to be used over multiple channels as described in the blog post 5 Reasons Web Components Are Perfect for Design Systems. In my example at https://mango-river-020e4d803.azurestaticapps.net/ I showcase the use of the Address Card component from ReactJS and Vue as well, and I show how Storybook can be used to document and showcase components.
Address card components used from ReactJS:
Address card components used from Vue:
Address card in Storybook:
Given the above capabilities, a nice overview of what we could achieve by adopting web components is given in the picture below:
In this blog post and the provided links I show how web components are a great candidate for building Sitecore SXA rendering variants that seamlessly integrate with existing Sitecore renderings and provide great SEO. The described approach could be the start of a design system with components that can be applied across your web applications from different web frameworks. So in my opinion web components are the silver bullet for Sitecore SXA… and beyond!
https://mango-river-020e4d803.azurestaticapps.net/ - web components sample + ReactJS + Vue + Storybook
https://github.com/svdoever/sxa-umbrella-webcomponents - GitHub repo of the above web site
Discuss on Twitter • Edit on GitHub
This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License. You are free to share and adapt this work for non-commercial purposes, provided you give appropriate credit, provide a link to the license, and indicate if changes were made. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc/4.0/.
Personal blog by Serge van den Oever - als je maar lol hebt...
X: @svdoever
LinkedIn: Serge van den Oever - articles on LinkedIn
GitHub: svdoever
Technology Consultant @ Macaw
2021-2024 Sitecore Technology MVP