JSS + TypeScript Sitecore Project Tips
Index
- TLDR;
- Glossary
- MVC Build Workflow
- Headless Build Workflow
- Key Questions for Architects and Dev Leads on Headless Projects
- Experience Editor
- Format on Save & Prettier
- TypeScript (And Why We Use It)
- Search
- Model and Type Generation
- Labels and Common Text
- Complex Components
- Nested Placeholder Components
- Testing
- Checklists
- Conclusion
TLDR;
If this is your first JSS + TS project, you will experience many challenges. Collaboration and communication is key. These types of projects can easily result in issues arising from different people / teams working in silos.
There are notable differences between a typical MVC project and a headless project, and architects / dev leads would be wise to understand how they differ and how to best manage them.
Make sure your team is well trained in Next.JS and TS. There are many ways things can go sideways if juniors are free to wreak havoc without oversight of someone who is well versed with the technologies.
Let's go!
Glossary
Here's the alphabet soup commonly used in this post:
- dev / devs: developer / developers
- FED: front end developer
- BED: back end developer
- TS: TypeScript
- JSS: Sitecore JavaScript Services
- SSG: static site generation
- EE: Experience Editor
- PR: pull request
- DTO: data transfer object
- MVC: model / view / controller (technical architecture)
MVC Build Workflow
In the MVC projects I've seen, devs either build the full stack on their own, or BEDs and FEDs collaborate to build the site.
In the case of a full stack dev handling everything on their own, when they're developing a component, they're going to set up:
- Sitecore templates / renderings
- Controller
- Model
- View Model
- View (
.cshtml
) - Styles (
.scss
) - JavaScript
Some organizations will separate the concerns of BEDs and FEDs, and in the case of a MVC project, the FED will build the front end of the component, perhaps in the form of .twig
files with some mechanism for dynamic mock data, which would then be taken by the BEDs and converted to .cshtml
files. The key is that there is some collaboration between BEDs and FEDs, but at the end of the day, the BEDs are the ones who are responsible for the final product, and they are likely to have a lot of visibility into the FEDs work, e.g. if a FED makes a change, the BED needs to be made aware of it so they can update the backend code.
In this workflow, FEDs can often get away with not testing their code against EE, because the BEDs will likely be the ones to handle that.
This changes in a headless build.
Headless Build Workflow
In headless, lines are beginning to blur between FEDs and BEDs. Headless builds differ from MVC builds in that the FEDs working on the Next.JS app are now more likely to have the final say on what end users experience, and they are more likely to be a source of bugs; especially if the FEDs are new to the stack and the intricacies of Sitecore. Headless sites push a lot of the logic to the JS side, and the JS side is more likely to be the source of bugs and issues.
This new paradigm calls for FEDs to adopt skills of BEDs, and vice versa.
In a headless build, there are numerous approaches, be they "Sitecore first" or "code first", but the point is that unless you have true full stack devs, a project will generally be comprised of two distinct phases / groups:
- The Sitecore devs set up the whole backend and get a Sitecore instance deployed somewhere that the FEDs can access. Then, the FEDs begin development.
- The FEDs start building the Next.JS app with mock data using a tool such as Storybook so they don't need to wait for a Sitecore instance that they can code against. There is a good video on this approach here: https://www.youtube.com/watch?v=kZ1d1e825wI. Note that there are already sample projects with all of this set up for you.
- Some hybrid of the first two.
The hardest part of this is coordinating teams and managing the "contracts" between the BEDs and the FEDs; that is, are the BEDs providing the FEDs with what they need, and vice versa? To make this more challenging, this often happens asynchronously and in parallel, so it's not uncommon for the FEDs to be working on a component that the BEDs haven't finished yet, or vice versa.
Key Questions for Architects and Dev Leads on Headless Projects
The following questions should help reveal the most challenging issues that arise on headless projects:
- Do FEDs understand the business requirements well enough to finish components to completion? In headless builds, more business requirements shift to the FEDs.
- Are BEDs building functionality such that the FEDs can easily integrate it into the frontend?
- Are FEDs building components that the BEDs can easily integrate into the backend?
- Are features built to be flexible enough such that they don't require back and forth between FEDs and DEVs and thus requiring numerous revisions?
- Are BEDs setting up the FEDs for success, and vice versa?
- Are front end and back end features clearly communicated / documented?
- Are FEDs and BEDs communicating frequently and effectively?
- Is the upstream development environment easily to deploy to, for both BEDs and FEDs? As in, is DevOps (or lack thereof) a bottleneck that slows down development? A good way to think of this is worst case vs. best case. Worst case would entirely manual code and item deployments. Best case would be a CI/CD pipeline that automatically and rapidly deploys code and items to the upstream environment.
- Is it easy for developers to find the error logs of the upstream development environment?
- Are developers getting notified of code errors both locally (e.g. linting / builds) and in the upstream environment (via some error logger tool)? In my experience, if errors notifications aren't pushed to developers, they will go unnoticed and unfixed, or they will show up during QA, which is a much more expensive time to fix them.
- Do FEDs understand how to build EE compatible components?
Experience Editor
Every time I have spoken with a client that wasn't interested in EE support, it's because they've been burned in the past by an implementation partner who royally screwed up or neglected EE entirely. As slow and clunky as it can be, EE can be a wonderful tool for content editors, and it's a shame that it's so often neglected.
In this moment, I now realize that headless Next.JS / TS sites will lead to a whole new generation of sites with a broken EE experience. EE can be harder than ever to get right, due to the increased complexity of the frontend and quirks of React / Next.JS (I'm looking at you, useEffect()
!).
Simple principles will keep you on the happy path:
- Ensure that your FEDs can easily test their components in EE as they develop. FEDs not having local EE access can snowball quickly into an entire site where EE doesn't work anywhere. See this guide on setting it up.
- Plenty of null checks and defensive programming.
- Being thoughtful about conditional rendering of elements in EE. Event listener bindings can be broken, if conditional rendering is done incorrectly. A classic example is:
- When using
useEffect()
don't ignore dependency array warnings. If you're getting a warning, you're probably doing something wrong. - Using a component checklist to ensure that components are EE compatible.
- Proper use of placeholders and datasources. For example: in an accordion component, the accordion items should be datasourced, and the accordion component itself should be a placeholder that accepts accordion item renderings. This allows content editors to add / remove / reorder accordion items as needed.
- Tasteful use of edit frames and custom experience buttons.
- Styles that are compatible with EE (particularly, the ribbon menu).
Format on Save & Prettier
I recommend having editor configs in place for both BEDs and FEDs to ensure that code style is consistent. I find that it also speeds up development quite a bit when I can simply save a file instead of having to manually format it.
TypeScript (And Why We Use It)
Avoid building with plain JS at all costs (unless time to market is a top concern). Every time I have audited a solution built in plain JS, I have always found hoards of bugs that could have been caught at compile time with TS.
Building a site with plain JS is like writing a book without spell check or an editor.
Dear dairy... Today I went to the pubic library...
The whole reason we use TS is for type safety and to catch errors at compile time. If you're not using TS, you're not getting the full benefit of the stack. This is particularly important for null value handling, which is such a common source of issues in Sitecore builds.
If it's your first time working with TS, it's likely going to slow you down at first, but it's well worth it.
Here are a few important tips:
- Understand of equivalency and truth checks in TS. That is, the subtleties of
typeof undefined
,'undefined'
,null
,0
,'0'
,true
,'true'
,false
,'false'
,''
,[]
,{}
,NaN
,==
vs.===
, and shallow vs. deep object comparisons. - Understand how object copying works. As in, shallow vs. deep. This can easily sneak past you and leave you with nasty bugs that are hard to track down. Note that there are new features in JS that can help with this, such as structured cloning and
Object.assign
.
Search
Depending on how you build it, website search can be very difficult, and the functionality poses many project risks in terms of time and complexity.
Tips for search:
- Take the time to define what data the FEDs need and in what format to build out search functionality.
- In which areas is case sensitivity of keys and values important? Remember that TS is case sensitive with regards to keys...
- How will you handle query string state? Will you serialize it? How will you handle the serialization? Will you use a library? Will you write your own? Will you use a combination of both?
- Consider the complexities of different search fields such as AND/OR logic across multiple fields, multi / single select fields, typeahead, dropdown, freeform text, checkboxes, radio buttons, etc.
- Should facets be editable via Sitecore items?
- Order of operations -- render the components, ping API for facets, generic search (or targeted search if URL contains params)
- Serialization of query string in controller (if using API controllers). As in, comma separated vs. models and duplication of keys and the implications for URL length.
- Do you understand how to use NextRouter? You probably should if you are managing search state in your URL. Next uses
window.history.state
to keep track of history. Manually modifying this can interfere with next routing and make your search functionality very buggy in terms of forward/backward buttons.
Model and Type Generation
Take some time to figure out how you're going to generate your models and types. Manually? Automatically? How do you ensure that everyone / everything is in sync? Is it obvious to all developers how to sync and inspect the data structures? If you can fully automate the syncing of models and types, do it.
For more ideas on this topic, see my post: Automatic TypeScript Model Generation With TDS.
Labels and Common Text
- Make sure you're using the Sitecore dictionary in your Next.JS code.
- Should common labels be driven via Standard Values? Dictionary items? Custom settings items? In my experience, the best approach is to use a combination of all three. For example, you might have a common label that is used in a component that is used in many different places. You might want to use a standard value for the component, a dictionary item for the label, and a custom settings item for the default value of the label. This allows you to override the label in the component, and to override the default value of the label in the settings item. This is a very powerful approach that allows you to have a lot of flexibility in how you manage your labels.
- How and where do you handle label overrides?
- How does the above jive with data imports (content migrations) and branch templates?
Complex Components
Be particularly careful with complex components. These are components that have a lot of moving parts, and are often cross-feature. For example, a "Related Content" component that has a lot of different options and can be used in many different places. These components can be very difficult to build and maintain, and can be a source of many bugs. These types of components require solid planning and coordination between BEDs and FEDs. Using tools such as Storybook helps a lot, too.
Nested Placeholder Components
Nested placeholder components (accordions, tabs, etc.) are also tricky in their own way. Make sure your devs understand the different approaches to passing/reading/updating information up and down the hierarchy: https://doc.sitecore.com/xp/en/developers/hd/21/sitecore-headless-development/working-with-placeholders-in-a-jss-next-js-app.html
Testing
This is so important as you settle into this new paradigm. Your subsequent implementations will look to your first one as a model. It'd be a shame if you set a bad example for your future projects! Other thoughts:
- Set up tests as early as possible, even if the tests are basic. There are so many fantastic test frameworks, sample boilerplate, and you can use generative AI to generate test cases for you.
- Architects / dev leads should be spot checking throughout the project.
- Devs should be testing their own work using a component checklist.
- You should have BOTH server side and client side error logging on upstream environments, and you should actually review them.
- Storybook and similar tools allow you to easily test variants much faster than you can in Sitecore. Use them! If done correctly, it can save you a lot of time.
Checklists
In headless projects, it's so helpful for devs to have checklists to run through before shipping. I encourage you to check out my posts on checklists:
- Bulletproof Components (Part 1): Front End Checklist
- Bulletproof Components (Part 2): Back End Checklist
- Bulletproof Components (Part 3): General Checklist
Here's a specific example that comes up constantly in headless projects: which fields should display as raw HTML? Have you done the analysis on all the fields / data to determine which fields should and should not display as raw HTML?
Conclusion
I believe we're at a crucial point in the evolution of Sitecore. The headless paradigm is here to stay, and it's only going to get more popular. It's important that we get it right, and continue to add to the collective knowledge of the community. If this post helped you, pay it forward by sharing your own learnings!
Keep shipping & TypeScripting,
MG