For last couple of months I worked on large-scale Sitecore JSS implementation, with 100+ components, 10+ devs, started from scratch. It was a big change for me, as well as for entire team working only on Sitecore MVC in the past. Here are some of my thoughts, which may be useful if you start your JSS project:
Information Architecture is the Key to Success
- Headless with layout service forces you to think about content structure, when accessing various content parts is no longer just a database call (which is most likely cached anyway). With well structured content you can avoid a lot of custom .NET code, but also think if content is easy to use by front-end application, but still easy to manage for content authors (SXA helps a lot with this).
- Avoid data redundancy in layout service. For some scenarios you might find big chunks of same item’s JSON re-used by different components. If it’s common, check next point to reduce size of JSON.
Example: we had a widget component displayed on every page allowing quick booking of venues. Widget data source template contains Tree List, where content authors can pick venue items which they want to sell via website. Venue item template has a lot of properties. Because layout service resolve nested items from Tree List field, data returned by layout service for this widget is quite big. Same venue items are also used as data source in different component displayed on some pages, so layout service return it again.
Example: it’s common to have things like GTM code and AddThis widget ID (for social sharing) configured in Sitecore item:
Code-first or Sitecore-first? Contract-first with Storybook
- In the .NET focused companies, work is usually divided between different people: back-end (working with Sitecore items and .NET code) and front-end developers (creating html, css, js). Traditionally in Sitecore project, front-end developers (or external agencies) were creating static-html version of the page, which was later transformed by back-end developers (split into .cshtml components with logic to integrate with Sitecore). This approach had many drawbacks: BE needs to wait for FE to implement components and who is responsible for issues in front-end code later? One of the reasons why we started working with JSS was to avoid such problems.
Due to the people skill-set in the team (and some others factors, like complex requirements for custom Sitecore field types) we couldn’t really leverage JSS code-first approach and used Sitecore-first from the beginning. In Sitecore-first the dependency is inverted: FE needs to wait for BE developers, so layout service returns proper JSON. To solve this issues we used contract-first approach and Storybook, with contract (data template fields) documented before the implementation.
The issue with contract-first is that there is no ideal way to document and share it with the teams. In our case we used wiki, but it was redundant to data templates in Sitecore and required additional work to track later changes. Do you know better approach or used better tools for defining contracts?
Since we didn’t deploy item definitions from JSS to Sitecore, Storybook replaced for us code-first. By writing stories which contain different variants of contract (for example with optional field, without optional field, with short, or long content) and different viewports (mobile, tablet, desktop), front-end developers can test how the components behave in different scenarios, without involving Sitecore.
Example: Storybook stories in different viewports and different variants:
- Storyshots with Puppeteer for visual comparison testing.
How it works? Puppeteer launches a web browser (Chrome) internally and takes a screenshot of each of your storybook story. Then Storyshots compares screenshots with baseline snapshots and shows the differences. For us baseline was the verified snapshot saved to file system and pushed to the repository. Then tests can be executed for example, when checking pull requests. If screenshots don’t match, developers see that their push caused change in markup, if it was intended they need to update baseline, otherwise it means a bug. Pull request can’t be accepted if there is a difference.
One downside is that Storyshots with puppeteer generates different screenshots on different OS. We had this issue cause some of the team works on Macs, while others on Windows. To address it, create scripts to running the tests on Linux Docker container, which is supported on every OS. For CI on Azure Pipeline it’s not an issue, cause you can choose between Linux or Windows build agent.
Example: Failed Storyshot test output shows the difference between baseline and current state (in this case difference is with images vertical alignment):
Don’t overcomplicate .Net Helix
- Create set of generic content resolver, it should cover requirements for the most of the components. If not, your information architecture may not be right. For content resolvers:
- Use built-in resolvers from /sitecore/system/Modules/Layout Service/Rendering Contents Resolvers
- Create your own generic resolvers, for example to return both data source item and its children
- Use GraphQL if you need data from not related items in a component. GraphQL in Sitecore is pretty simple to use (after you learn how it works :)).
- Create one Helix .NET project to serialize items for majority of the components, even if those components are different business features. In JSS most of the components are .NET code-less, just:
- data source template item,
- rendering parameters template item,
- rendering definition item with usage of generic content resolver or integrated GraphQL query
- placeholder settings
- Create Helix Feature .NET projects only for components with complex logic which needs custom content resolver, needs controller for API calls, or needs data from external systems.
- Glass Mapper not really needed in JSS. To avoid hard-coded templates and field IDs in your .NET code, auto-generate structures upon solution build with Leprechaun.
- Auto-deploy .NET code upon solution build with Helix Publishing Pipeline
Front-End Performance as a Feature
- Cache Sitecore components by default in rendering definition items (*for personalized components check SXA)
- Use Dianoga for image compression, it works with JSS (just enable CDN mode).
- Follow JSS app performance best practices from Uniform team.
SXA for JSS can offer more
- Out-of the box: Multi-site, fast tenants & sites setup with item based configuration.
- Out-of the box: Support for local and shared data sources for content authors.
- Inherit from SXA ICacheable template in your rendering parameters template for every component to allow content authors to overwrite cache settings for personalized components.
- Support SXA grid system: part 1 and part 2
- Support SXA column splitter: check previous blog
- Implement your own generic SXA-like components set.
- Sitecore Forms works in JSS but the implementation is React only for:
– out-of the box and custom form fields support,
– field value providers,
– standard and custom submit actions.
There are missing features though (forms conditions), hopefully will be addressed by Sitecore in the future.
- Storybook: https://storybook.js.org/
- Storyshots: https://storybook.js.org/docs/react/workflows/snapshot-testing/
- Puppeteer https://www.npmjs.com/package/@storybook/addon-storyshots-puppeteer
- HPP: https://github.com/richardszalay/helix-publishing-pipeline
- Leprechaun: https://github.com/blipson89/Leprechaun
- Dianoga: https://github.com/kamsar/Dianoga
- Alex Shyba presentation about JSS performance from SUGCON 2020: https://www.youtube.com/watch?v=qrXo1KqETtw